mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-19 15:45:13 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ef8826f33 | |||
| 61b70efb4c | |||
| 63977ea353 | |||
| ae57d3b7c3 | |||
| 8d76087bed | |||
| 0e90deafb5 | |||
| 39d45124df | |||
| 895403a0b5 | |||
| 8cb177cb2c | |||
| eaf802e07d | |||
| 1ef242a17e | |||
| 7adb4fa2db | |||
| 5c5ee598a2 | |||
| a4ae6f2097 | |||
| fc5a56c15b | |||
| 74112bfd06 | |||
| ab90cd7359 | |||
| 5b0f1697f1 | |||
| 9c7298c17a | |||
| 39abf838fa | |||
| 04b44f4595 | |||
| a0decdac28 | |||
| f77f4b0cf7 |
@@ -17,3 +17,4 @@ enabled = true
|
||||
runtime_version = "3.x.x"
|
||||
max_line_length = 99
|
||||
skip_doc_coverage = ["module", "magic", "init", "nonpublic"]
|
||||
cyclomatic_complexity_threshold = "high"
|
||||
|
||||
@@ -16,9 +16,9 @@ jobs:
|
||||
|
||||
- name: Fetch Dependabot metadata
|
||||
id: dependabot-metadata
|
||||
uses: dependabot/fetch-metadata@v1.6.1
|
||||
uses: dependabot/fetch-metadata@v1.6.0
|
||||
|
||||
- uses: actions/checkout@v3.5.2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
|
||||
@@ -35,4 +35,4 @@ jobs:
|
||||
with:
|
||||
message: 'Update version number in other files'
|
||||
committer_name: GitHub Actions
|
||||
committer_email: 41898282+github-actions[bot]@users.noreply.github.com
|
||||
committer_email: 41898282+github-actions[bot]@users.noreply.github.com
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
name: Bot API Tests
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
schedule:
|
||||
# Run monday and friday morning at 03:07 - odd time to spread load on GitHub Actions
|
||||
- cron: '7 3 * * 1,5'
|
||||
|
||||
jobs:
|
||||
check-conformity:
|
||||
name: check-conformity
|
||||
runs-on: ${{matrix.os}}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.11]
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -W ignore -m pip install --upgrade pip
|
||||
python -W ignore -m pip install -r requirements.txt
|
||||
python -W ignore -m pip install -r requirements-opts.txt
|
||||
python -W ignore -m pip install -r requirements-dev.txt
|
||||
- name: Compare to official api
|
||||
run: |
|
||||
pytest -v tests/test_official.py --junit-xml=.test_report_official.xml
|
||||
exit $?
|
||||
env:
|
||||
TEST_OFFICIAL: "true"
|
||||
shell: bash --noprofile --norc {0}
|
||||
|
||||
- name: Test Summary
|
||||
id: test_summary
|
||||
uses: test-summary/action@v2.1
|
||||
if: always() # always run, even if tests fail
|
||||
with:
|
||||
paths: .test_report_official.xml
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
name: test-type-completeness
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- run: git fetch --depth=1 # https://github.com/actions/checkout/issues/329#issuecomment-674881489
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
|
||||
@@ -16,11 +16,11 @@ jobs:
|
||||
runs-on: ${{matrix.os}}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12.0-rc.1']
|
||||
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
@@ -92,38 +92,3 @@ jobs:
|
||||
env_vars: OS,PYTHON
|
||||
name: ${{ matrix.os }}-${{ matrix.python-version }}
|
||||
fail_ci_if_error: true
|
||||
|
||||
test_official:
|
||||
name: test-official
|
||||
runs-on: ${{matrix.os}}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.11]
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -W ignore -m pip install --upgrade pip
|
||||
python -W ignore -m pip install -r requirements.txt
|
||||
python -W ignore -m pip install -r requirements-opts.txt
|
||||
python -W ignore -m pip install -r requirements-dev.txt
|
||||
- name: Compare to official api
|
||||
run: |
|
||||
pytest -v tests/test_official.py --junit-xml=.test_report_official.xml
|
||||
exit $?
|
||||
env:
|
||||
TEST_OFFICIAL: "true"
|
||||
shell: bash --noprofile --norc {0}
|
||||
|
||||
- name: Test Summary
|
||||
id: test_summary
|
||||
uses: test-summary/action@v2.1
|
||||
if: always() # always run, even if tests fail
|
||||
with:
|
||||
paths: .test_report_official.xml
|
||||
@@ -6,7 +6,7 @@ ci:
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.7.0
|
||||
rev: 23.9.1
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
@@ -17,7 +17,7 @@ repos:
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: https://github.com/PyCQA/pylint
|
||||
rev: v3.0.0a6
|
||||
rev: v3.0.0
|
||||
hooks:
|
||||
- id: pylint
|
||||
files: ^(telegram|examples)/.*\.py$
|
||||
@@ -29,13 +29,13 @@ repos:
|
||||
|
||||
additional_dependencies:
|
||||
- httpx~=0.24.1
|
||||
- tornado~=6.2
|
||||
- tornado~=6.3.3
|
||||
- APScheduler~=3.10.4
|
||||
- cachetools~=5.3.1
|
||||
- aiolimiter~=1.1.0
|
||||
- . # this basically does `pip install -e .`
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v1.4.1
|
||||
rev: v1.5.1
|
||||
hooks:
|
||||
- id: mypy
|
||||
name: mypy-ptb
|
||||
@@ -45,7 +45,7 @@ repos:
|
||||
- types-cryptography
|
||||
- types-cachetools
|
||||
- httpx~=0.24.1
|
||||
- tornado~=6.2
|
||||
- tornado~=6.3.3
|
||||
- APScheduler~=3.10.4
|
||||
- cachetools~=5.3.1
|
||||
- aiolimiter~=1.1.0
|
||||
@@ -57,12 +57,12 @@ repos:
|
||||
- --no-strict-optional
|
||||
- --follow-imports=silent
|
||||
additional_dependencies:
|
||||
- tornado~=6.2
|
||||
- tornado~=6.3.3
|
||||
- APScheduler~=3.10.4
|
||||
- cachetools~=5.3.1
|
||||
- . # this basically does `pip install -e .`
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.10.1
|
||||
rev: v3.13.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
files: ^(telegram|examples|tests|docs)/.*\.py$
|
||||
@@ -77,14 +77,14 @@ repos:
|
||||
- --diff
|
||||
- --check
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: 'v0.0.281'
|
||||
rev: 'v0.0.292'
|
||||
hooks:
|
||||
- id: ruff
|
||||
name: ruff
|
||||
files: ^(telegram|examples|tests)/.*\.py$
|
||||
additional_dependencies:
|
||||
- httpx~=0.24.1
|
||||
- tornado~=6.2
|
||||
- tornado~=6.3.3
|
||||
- APScheduler~=3.10.4
|
||||
- cachetools~=5.3.1
|
||||
- aiolimiter~=1.1.0
|
||||
|
||||
+50
@@ -4,6 +4,56 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Version 20.6
|
||||
============
|
||||
|
||||
*Released 2023-10-03*
|
||||
|
||||
This is the technical changelog for version 20.6. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`__.
|
||||
|
||||
Major Changes
|
||||
-------------
|
||||
|
||||
- Drop Backward Compatibility Layer Introduced in :pr:`3853` (API 6.8) (:pr:`3873`)
|
||||
- Full Support for Bot API 6.9 (:pr:`3898`)
|
||||
|
||||
New Features
|
||||
------------
|
||||
|
||||
- Add Rich Equality Comparison to ``WriteAccessAllowed`` (:pr:`3911` closes :issue:`3909`)
|
||||
- Add ``__repr__`` Methods Added in :pr:`3826` closes :issue:`3770` to Sphinx Documentation (:pr:`3901` closes :issue:`3889`)
|
||||
- Add String Representation for Selected Classes (:pr:`3826` closes :issue:`3770`)
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Add Support Python 3.12 (:pr:`3915`)
|
||||
- Documentation Improvements (:pr:`3910`)
|
||||
|
||||
Internal Changes
|
||||
----------------
|
||||
|
||||
- Verify Type Hints for Bot Method & Telegram Class Parameters (:pr:`3868`)
|
||||
- Move Bot API Tests to Separate Workflow File (:pr:`3912`)
|
||||
- Fix Failing ``file_size`` Tests (:pr:`3906`)
|
||||
- Set Threshold for DeepSource’s PY-R1000 to High (:pr:`3888`)
|
||||
- One-Time Code Formatting Improvement via ``--preview`` Flag of ``black`` (:pr:`3882`)
|
||||
- Move Dunder Methods to the Top of Class Bodies (:pr:`3883`)
|
||||
- Remove Superfluous ``Defaults.__ne__`` (:pr:`3884`)
|
||||
|
||||
Dependency Updates
|
||||
------------------
|
||||
|
||||
- ``pre-commit`` autoupdate (:pr:`3876`)
|
||||
- Update ``pre-commit`` Dependencies (:pr:`3916`)
|
||||
- Bump ``actions/checkout`` from 3 to 4 (:pr:`3914`)
|
||||
- Update ``httpx`` requirement from ~=0.24.1 to ~=0.25.0 (:pr:`3891`)
|
||||
- Bump ``furo`` from 2023.8.19 to 2023.9.10 (:pr:`3890`)
|
||||
- Bump ``sphinx`` from 7.2.5 to 7.2.6 (:pr:`3892`)
|
||||
- Update ``tornado`` requirement from ~=6.2 to ~=6.3.3 (:pr:`3675`)
|
||||
- Bump ``pytest`` from 7.4.0 to 7.4.2 (:pr:`3881`)
|
||||
|
||||
|
||||
Version 20.5
|
||||
============
|
||||
*Released 2023-09-03*
|
||||
|
||||
+3
-3
@@ -14,7 +14,7 @@
|
||||
:target: https://pypi.org/project/python-telegram-bot/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-6.8-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-6.9-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API versions
|
||||
|
||||
@@ -93,7 +93,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju
|
||||
Telegram API support
|
||||
====================
|
||||
|
||||
All types and methods of the Telegram Bot API **6.8** are supported.
|
||||
All types and methods of the Telegram Bot API **6.9** are supported.
|
||||
|
||||
Installing
|
||||
==========
|
||||
@@ -152,7 +152,7 @@ PTB can be installed with optional dependencies:
|
||||
* ``pip install "python-telegram-bot[socks]"`` installs `httpx[socks] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to work behind a Socks5 server.
|
||||
* ``pip install "python-telegram-bot[http2]"`` installs `httpx[http2] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to use HTTP/2.
|
||||
* ``pip install "python-telegram-bot[rate-limiter]"`` installs `aiolimiter~=1.1.0 <https://aiolimiter.readthedocs.io/en/stable/>`_. Use this, if you want to use ``telegram.ext.AIORateLimiter``.
|
||||
* ``pip install "python-telegram-bot[webhooks]"`` installs the `tornado~=6.2 <https://www.tornadoweb.org/en/stable/>`_ library. Use this, if you want to use ``telegram.ext.Updater.start_webhook``/``telegram.ext.Application.run_webhook``.
|
||||
* ``pip install "python-telegram-bot[webhooks]"`` installs the `tornado~=6.3.3 <https://www.tornadoweb.org/en/stable/>`_ library. Use this, if you want to use ``telegram.ext.Updater.start_webhook``/``telegram.ext.Application.run_webhook``.
|
||||
* ``pip install "python-telegram-bot[callback-data]"`` installs the `cachetools~=5.3.1 <https://cachetools.readthedocs.io/en/latest/>`_ library. Use this, if you want to use `arbitrary callback_data <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Arbitrary-callback_data>`_.
|
||||
* ``pip install "python-telegram-bot[job-queue]"`` installs the `APScheduler~=3.10.4 <https://apscheduler.readthedocs.io/en/3.x/>`_ library and enforces `pytz>=2018.6 <https://pypi.org/project/pytz/>`_, where ``pytz`` is a dependency of ``APScheduler``. Use this, if you want to use the ``telegram.ext.JobQueue``.
|
||||
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@
|
||||
:target: https://pypi.org/project/python-telegram-bot-raw/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-6.8-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-6.9-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API versions
|
||||
|
||||
@@ -89,7 +89,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju
|
||||
Telegram API support
|
||||
====================
|
||||
|
||||
All types and methods of the Telegram Bot API **6.8** are supported.
|
||||
All types and methods of the Telegram Bot API **6.9** are supported.
|
||||
|
||||
Installing
|
||||
==========
|
||||
|
||||
@@ -194,7 +194,7 @@ class AdmonitionInserter:
|
||||
)
|
||||
except NotImplementedError as e:
|
||||
raise NotImplementedError(
|
||||
f"Error generating Sphinx 'Available in' admonition "
|
||||
"Error generating Sphinx 'Available in' admonition "
|
||||
f"(admonition_inserter.py). Class {name_of_class_in_attr} present in "
|
||||
f"attribute {target_attr} of class {name_of_inspected_class_in_docstr}"
|
||||
f" could not be resolved. {str(e)}"
|
||||
@@ -237,7 +237,7 @@ class AdmonitionInserter:
|
||||
)
|
||||
except NotImplementedError as e:
|
||||
raise NotImplementedError(
|
||||
f"Error generating Sphinx 'Available in' admonition "
|
||||
"Error generating Sphinx 'Available in' admonition "
|
||||
f"(admonition_inserter.py). Class {name_of_class_in_prop} present in "
|
||||
f"property {prop_name} of class {name_of_inspected_class_in_docstr}"
|
||||
f" could not be resolved. {str(e)}"
|
||||
@@ -269,7 +269,7 @@ class AdmonitionInserter:
|
||||
)
|
||||
except NotImplementedError as e:
|
||||
raise NotImplementedError(
|
||||
f"Error generating Sphinx 'Returned in' admonition "
|
||||
"Error generating Sphinx 'Returned in' admonition "
|
||||
f"(admonition_inserter.py). {cls}, method {method_name}. "
|
||||
f"Couldn't resolve type hint in return annotation {ret_annot}. {str(e)}"
|
||||
)
|
||||
@@ -342,7 +342,7 @@ class AdmonitionInserter:
|
||||
)
|
||||
except NotImplementedError as e:
|
||||
raise NotImplementedError(
|
||||
f"Error generating Sphinx 'Use in' admonition "
|
||||
"Error generating Sphinx 'Use in' admonition "
|
||||
f"(admonition_inserter.py). {cls}, method {method_name}, parameter "
|
||||
f"{param}: Couldn't resolve type hint {param.annotation}. {str(e)}"
|
||||
)
|
||||
|
||||
@@ -18,25 +18,41 @@
|
||||
import inspect
|
||||
|
||||
keyword_args = [
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.read_timeout: Value to pass to "
|
||||
":paramref:`telegram.request.BaseRequest.post.read_timeout`. Defaults to {read_timeout}.",
|
||||
(
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.read_timeout: Value to pass to "
|
||||
":paramref:`telegram.request.BaseRequest.post.read_timeout`. Defaults to {read_timeout}."
|
||||
),
|
||||
":kwtype _sphinx_paramlinks_telegram.Bot.{method}.read_timeout: {read_timeout_type}, optional",
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.write_timeout: Value to pass to "
|
||||
":paramref:`telegram.request.BaseRequest.post.write_timeout`. Defaults to {write_timeout}.",
|
||||
":kwtype _sphinx_paramlinks_telegram.Bot.{method}.write_timeout: :obj:`float` | :obj:`None`, "
|
||||
"optional",
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.connect_timeout: Value to pass to "
|
||||
":paramref:`telegram.request.BaseRequest.post.connect_timeout`. Defaults to "
|
||||
":attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.",
|
||||
":kwtype _sphinx_paramlinks_telegram.Bot.{method}.connect_timeout: :obj:`float` | "
|
||||
":obj:`None`, optional",
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.pool_timeout: Value to pass to "
|
||||
":paramref:`telegram.request.BaseRequest.post.pool_timeout`. Defaults to "
|
||||
":attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.",
|
||||
":kwtype _sphinx_paramlinks_telegram.Bot.{method}.pool_timeout: :obj:`float` | :obj:`None`, "
|
||||
"optional",
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.api_kwargs: Arbitrary keyword arguments "
|
||||
"to be passed to the Telegram API.",
|
||||
(
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.write_timeout: Value to pass to "
|
||||
":paramref:`telegram.request.BaseRequest.post.write_timeout`. Defaults to {write_timeout}."
|
||||
),
|
||||
(
|
||||
":kwtype _sphinx_paramlinks_telegram.Bot.{method}.write_timeout: :obj:`float` |"
|
||||
" :obj:`None`, optional"
|
||||
),
|
||||
(
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.connect_timeout: Value to pass to "
|
||||
":paramref:`telegram.request.BaseRequest.post.connect_timeout`. Defaults to "
|
||||
":attr:`~telegram.request.BaseRequest.DEFAULT_NONE`."
|
||||
),
|
||||
(
|
||||
":kwtype _sphinx_paramlinks_telegram.Bot.{method}.connect_timeout: :obj:`float` | "
|
||||
":obj:`None`, optional"
|
||||
),
|
||||
(
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.pool_timeout: Value to pass to "
|
||||
":paramref:`telegram.request.BaseRequest.post.pool_timeout`. Defaults to "
|
||||
":attr:`~telegram.request.BaseRequest.DEFAULT_NONE`."
|
||||
),
|
||||
(
|
||||
":kwtype _sphinx_paramlinks_telegram.Bot.{method}.pool_timeout: :obj:`float` |"
|
||||
" :obj:`None`, optional"
|
||||
),
|
||||
(
|
||||
":keyword _sphinx_paramlinks_telegram.Bot.{method}.api_kwargs: Arbitrary keyword arguments"
|
||||
" to be passed to the Telegram API."
|
||||
),
|
||||
":kwtype _sphinx_paramlinks_telegram.Bot.{method}.api_kwargs: :obj:`dict`, optional",
|
||||
"",
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
sphinx==7.2.5
|
||||
sphinx==7.2.6
|
||||
sphinx-pypi-upload
|
||||
furo==2023.8.19
|
||||
furo==2023.9.10
|
||||
git+https://github.com/harshil21/furo-sphinx-search@v0.2.0.1
|
||||
sphinx-paramlinks==0.6.0
|
||||
sphinxcontrib-mermaid==0.9.2
|
||||
|
||||
+62
-52
@@ -21,9 +21,9 @@ author = "Leandro Toledo"
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = "20.5" # telegram.__version__[:3]
|
||||
version = "20.6" # telegram.__version__[:3]
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = "20.5" # telegram.__version__
|
||||
release = "20.6" # telegram.__version__
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
needs_sphinx = "6.1.3"
|
||||
@@ -96,7 +96,9 @@ linkcheck_ignore = [
|
||||
]
|
||||
linkcheck_allowed_redirects = {
|
||||
# Redirects to the default version are okay
|
||||
r"https://docs\.python-telegram-bot\.org/.*": r"https://docs\.python-telegram-bot\.org/en/[\w\d\.]+/.*",
|
||||
r"https://docs\.python-telegram-bot\.org/.*": (
|
||||
r"https://docs\.python-telegram-bot\.org/en/[\w\d\.]+/.*"
|
||||
),
|
||||
# pre-commit.ci always redirects to the latest run
|
||||
re.escape(
|
||||
"https://results.pre-commit.ci/latest/github/python-telegram-bot/python-telegram-bot/master"
|
||||
@@ -131,71 +133,79 @@ html_theme_options = {
|
||||
"admonition-title-font-size": "0.95rem",
|
||||
"admonition-font-size": "0.92rem",
|
||||
},
|
||||
"announcement": "PTB has undergone significant changes in v20. Please read the documentation "
|
||||
"carefully and also check out the transition guide in the "
|
||||
'<a href="https://github.com/python-telegram-bot/python-telegram-bot/wiki/'
|
||||
'Transition-guide-to-Version-20.0">wiki</a>.',
|
||||
"announcement": (
|
||||
"PTB has undergone significant changes in v20. Please read the documentation "
|
||||
"carefully and also check out the transition guide in the "
|
||||
'<a href="https://github.com/python-telegram-bot/python-telegram-bot/wiki/'
|
||||
'Transition-guide-to-Version-20.0">wiki</a>.'
|
||||
),
|
||||
"footer_icons": [
|
||||
{
|
||||
# Telegram channel logo
|
||||
"name": "Telegram Channel",
|
||||
"url": "https://t.me/pythontelegrambotchannel/",
|
||||
# Following svg is from https://react-icons.github.io/react-icons/search?q=telegram
|
||||
"html": '<svg stroke="currentColor" fill="currentColor" stroke-width="0" '
|
||||
'viewBox="0 0 16 16" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">'
|
||||
'<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.287 5.906c-.778.324-2.334.994'
|
||||
"-4.666 2.01-.378.15-.577.298-.595.442-.03.243.275.339.69.47l.175.055c.408.133."
|
||||
"958.288 1.243.294.26.006.549-.1.868-.32 2.179-1.471 3.304-2.214 3.374-2.23.0"
|
||||
"5-.012.12-.026.166.016.047.041.042.12.037.141-.03.129-1.227 1.241-1.846 1.81"
|
||||
"7-.193.18-.33.307-.358.336a8.154 8.154 0 0 1-.188.186c-.38.366-.664.64.015 1.08"
|
||||
"8.327.216.589.393.85.571.284.194.568.387.936.629.093.06.183.125.27.187.331.23"
|
||||
"6.63.448.997.414.214-.02.435-.22.547-.82.265-1.417.786-4.486.906-5.751a1.426 "
|
||||
"1.426 0 0 0-.013-.315.337.337 0 0 0-.114-.217.526.526 0 0 0-.31-.093c-.3.005-.7"
|
||||
'63.166-2.984 1.09z"></path></svg>',
|
||||
"html": (
|
||||
'<svg stroke="currentColor" fill="currentColor" stroke-width="0" '
|
||||
'viewBox="0 0 16 16" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">'
|
||||
'<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.287 5.906c-.778.324-2.334.994'
|
||||
"-4.666 2.01-.378.15-.577.298-.595.442-.03.243.275.339.69.47l.175.055c.408.133."
|
||||
"958.288 1.243.294.26.006.549-.1.868-.32 2.179-1.471 3.304-2.214 3.374-2.23.0"
|
||||
"5-.012.12-.026.166.016.047.041.042.12.037.141-.03.129-1.227 1.241-1.846 1.81"
|
||||
"7-.193.18-.33.307-.358.336a8.154 8.154 0 0 1-.188.186c-.38.366-.664.64.015 1.08"
|
||||
"8.327.216.589.393.85.571.284.194.568.387.936.629.093.06.183.125.27.187.331.23"
|
||||
"6.63.448.997.414.214-.02.435-.22.547-.82.265-1.417.786-4.486.906-5.751a1.426 "
|
||||
"1.426 0 0 0-.013-.315.337.337 0 0 0-.114-.217.526.526 0 0 0-.31-.093c-.3.005-.7"
|
||||
'63.166-2.984 1.09z"></path></svg>'
|
||||
),
|
||||
"class": "",
|
||||
},
|
||||
{ # Github logo
|
||||
"name": "GitHub",
|
||||
"url": "https://github.com/python-telegram-bot/python-telegram-bot/",
|
||||
"html": '<svg stroke="currentColor" fill="currentColor" stroke-width="0" '
|
||||
'viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 '
|
||||
"2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.4"
|
||||
"9-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23"
|
||||
".82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 "
|
||||
"0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.2"
|
||||
"7 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.5"
|
||||
"1.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 "
|
||||
'1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z">'
|
||||
"</path></svg>",
|
||||
"html": (
|
||||
'<svg stroke="currentColor" fill="currentColor" stroke-width="0" '
|
||||
'viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 '
|
||||
"2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.4"
|
||||
"9-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23"
|
||||
".82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 "
|
||||
"0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.2"
|
||||
"7 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.5"
|
||||
"1.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 "
|
||||
'1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z">'
|
||||
"</path></svg>"
|
||||
),
|
||||
"class": "",
|
||||
},
|
||||
{ # PTB website logo - globe
|
||||
"name": "python-telegram-bot website",
|
||||
"url": "https://python-telegram-bot.org/",
|
||||
"html": '<svg stroke="currentColor" fill="currentColor" stroke-width="0" '
|
||||
'viewBox="0 0 16 16" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">'
|
||||
'<path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm7.5-6.923c-.67.204-1.335.82-1.887 '
|
||||
"1.855-.143.268-.276.56-.395.872.705.157 1.472.257 2.282.287V1.077zM4.249 3.53"
|
||||
"9c.142-.384.304-.744.481-1.078a6.7 6.7 0 0 1 .597-.933A7.01 7.01 0 0 0 3.051 "
|
||||
"3.05c.362.184.763.349 1.198.49zM3.509 7.5c.036-1.07.188-2.087.436-3.008a9.124 "
|
||||
"9.124 0 0 1-1.565-.667A6.964 6.964 0 0 0 1.018 7.5h2.49zm1.4-2.741a12.344 "
|
||||
"12.344 0 0 0-.4 2.741H7.5V5.091c-.91-.03-1.783-.145-2.591-.332zM8.5 5.09V7.5h"
|
||||
"2.99a12.342 12.342 0 0 0-.399-2.741c-.808.187-1.681.301-2.591.332zM4.51 8.5c.03"
|
||||
"5.987.176 1.914.399 2.741A13.612 13.612 0 0 1 7.5 10.91V8.5H4.51zm3.99 0v2.409"
|
||||
"c.91.03 1.783.145 2.591.332.223-.827.364-1.754.4-2.741H8.5zm-3.282 3.696c.12.31"
|
||||
"2.252.604.395.872.552 1.035 1.218 1.65 1.887 1.855V11.91c-.81.03-1.577.13-2.28"
|
||||
"2.287zm.11 2.276a6.696 6.696 0 0 1-.598-.933 8.853 8.853 0 0 1-.481-1.079 8.38 "
|
||||
"8.38 0 0 0-1.198.49 7.01 7.01 0 0 0 2.276 1.522zm-1.383-2.964A13.36 13.36 0 0 1"
|
||||
" 3.508 8.5h-2.49a6.963 6.963 0 0 0 1.362 3.675c.47-.258.995-.482 1.565-.667zm"
|
||||
"6.728 2.964a7.009 7.009 0 0 0 2.275-1.521 8.376 8.376 0 0 0-1.197-.49 8.853 "
|
||||
"8.853 0 0 1-.481 1.078 6.688 6.688 0 0 1-.597.933zM8.5 11.909v3.014c.67-.204 "
|
||||
"1.335-.82 1.887-1.855.143-.268.276-.56.395-.872A12.63 12.63 0 0 0 8.5 11.91zm"
|
||||
"3.555-.401c.57.185 1.095.409 1.565.667A6.963 6.963 0 0 0 14.982 8.5h-2.49a1"
|
||||
"3.36 13.36 0 0 1-.437 3.008zM14.982 7.5a6.963 6.963 0 0 0-1.362-3.675c-.47.25"
|
||||
"8-.995.482-1.565.667.248.92.4 1.938.437 3.008h2.49zM11.27 2.461c.177.334.339.6"
|
||||
"94.482 1.078a8.368 8.368 0 0 0 1.196-.49 7.01 7.01 0 0 0-2.275-1.52c.218.283.4"
|
||||
"18.597.597.932zm-.488 1.343a7.765 7.765 0 0 0-.395-.872C9.835 1.897 9.17 1.282 "
|
||||
'8.5 1.077V4.09c.81-.03 1.577-.13 2.282-.287z"></path></svg>',
|
||||
"html": (
|
||||
'<svg stroke="currentColor" fill="currentColor" stroke-width="0" '
|
||||
'viewBox="0 0 16 16" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">'
|
||||
'<path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm7.5-6.923c-.67.204-1.335.82-1.887 '
|
||||
"1.855-.143.268-.276.56-.395.872.705.157 1.472.257 2.282.287V1.077zM4.249 3.53"
|
||||
"9c.142-.384.304-.744.481-1.078a6.7 6.7 0 0 1 .597-.933A7.01 7.01 0 0 0 3.051 "
|
||||
"3.05c.362.184.763.349 1.198.49zM3.509 7.5c.036-1.07.188-2.087.436-3.008a9.124 "
|
||||
"9.124 0 0 1-1.565-.667A6.964 6.964 0 0 0 1.018 7.5h2.49zm1.4-2.741a12.344 "
|
||||
"12.344 0 0 0-.4 2.741H7.5V5.091c-.91-.03-1.783-.145-2.591-.332zM8.5 5.09V7.5h"
|
||||
"2.99a12.342 12.342 0 0 0-.399-2.741c-.808.187-1.681.301-2.591.332zM4.51 8.5c.03"
|
||||
"5.987.176 1.914.399 2.741A13.612 13.612 0 0 1 7.5 10.91V8.5H4.51zm3.99 0v2.409"
|
||||
"c.91.03 1.783.145 2.591.332.223-.827.364-1.754.4-2.741H8.5zm-3.282 3.696c.12.31"
|
||||
"2.252.604.395.872.552 1.035 1.218 1.65 1.887 1.855V11.91c-.81.03-1.577.13-2.28"
|
||||
"2.287zm.11 2.276a6.696 6.696 0 0 1-.598-.933 8.853 8.853 0 0 1-.481-1.079 8.38 "
|
||||
"8.38 0 0 0-1.198.49 7.01 7.01 0 0 0 2.276 1.522zm-1.383-2.964A13.36 13.36 0 0 1"
|
||||
" 3.508 8.5h-2.49a6.963 6.963 0 0 0 1.362 3.675c.47-.258.995-.482 1.565-.667zm"
|
||||
"6.728 2.964a7.009 7.009 0 0 0 2.275-1.521 8.376 8.376 0 0 0-1.197-.49 8.853 "
|
||||
"8.853 0 0 1-.481 1.078 6.688 6.688 0 0 1-.597.933zM8.5 11.909v3.014c.67-.204 "
|
||||
"1.335-.82 1.887-1.855.143-.268.276-.56.395-.872A12.63 12.63 0 0 0 8.5 11.91zm"
|
||||
"3.555-.401c.57.185 1.095.409 1.565.667A6.963 6.963 0 0 0 14.982 8.5h-2.49a1"
|
||||
"3.36 13.36 0 0 1-.437 3.008zM14.982 7.5a6.963 6.963 0 0 0-1.362-3.675c-.47.25"
|
||||
"8-.995.482-1.565.667.248.92.4 1.938.437 3.008h2.49zM11.27 2.461c.177.334.339.6"
|
||||
"94.482 1.078a8.368 8.368 0 0 0 1.196-.49 7.01 7.01 0 0 0-2.275-1.52c.218.283.4"
|
||||
"18.597.597.932zm-.488 1.343a7.765 7.765 0 0 0-.395-.872C9.835 1.897 9.17 1.282 "
|
||||
'8.5 1.077V4.09c.81-.03 1.577-.13 2.282-.287z"></path></svg>'
|
||||
),
|
||||
"class": "",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -4,4 +4,4 @@ Bot
|
||||
.. autoclass:: telegram.Bot
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:special-members: __reduce__, __deepcopy__
|
||||
:special-members: __repr__, __reduce__, __deepcopy__
|
||||
@@ -4,3 +4,4 @@ Application
|
||||
.. autoclass:: telegram.ext.Application
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:special-members: __repr__
|
||||
|
||||
@@ -4,3 +4,4 @@ BaseHandler
|
||||
.. autoclass:: telegram.ext.BaseHandler
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:special-members: __repr__
|
||||
|
||||
@@ -4,3 +4,4 @@ ConversationHandler
|
||||
.. autoclass:: telegram.ext.ConversationHandler
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:special-members: __repr__
|
||||
|
||||
@@ -4,3 +4,4 @@ ExtBot
|
||||
.. autoclass:: telegram.ext.ExtBot
|
||||
:show-inheritance:
|
||||
:members: insert_callback_data, defaults, rate_limiter, initialize, shutdown, callback_data_cache
|
||||
:special-members: __repr__
|
||||
|
||||
@@ -4,4 +4,4 @@ Job
|
||||
.. autoclass:: telegram.ext.Job
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:special-members: __call__
|
||||
:special-members: __call__, __repr__
|
||||
|
||||
@@ -4,3 +4,4 @@ JobQueue
|
||||
.. autoclass:: telegram.ext.JobQueue
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:special-members: __repr__
|
||||
|
||||
@@ -4,3 +4,4 @@ Updater
|
||||
.. autoclass:: telegram.ext.Updater
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:special-members: __repr__
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""This example showcases how PTBs "arbitrary callback data" feature can be used.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -115,8 +115,7 @@ async def count_click(update: Update, context: CustomContext) -> None:
|
||||
async def print_users(update: Update, context: CustomContext) -> None:
|
||||
"""Show which users have been using this bot."""
|
||||
await update.message.reply_text(
|
||||
"The following user IDs have used this bot: "
|
||||
f'{", ".join(map(str, context.bot_user_ids))}'
|
||||
f"The following user IDs have used this bot: {', '.join(map(str, context.bot_user_ids))}"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""Bot that explains Telegram's "Deep Linking Parameters" functionality.
|
||||
@@ -57,8 +57,7 @@ async def deep_linked_level_1(update: Update, context: ContextTypes.DEFAULT_TYPE
|
||||
bot = context.bot
|
||||
url = helpers.create_deep_linked_url(bot.username, SO_COOL)
|
||||
text = (
|
||||
"Awesome, you just accessed hidden functionality! "
|
||||
"Now let's get back to the private chat."
|
||||
"Awesome, you just accessed hidden functionality! Now let's get back to the private chat."
|
||||
)
|
||||
keyboard = InlineKeyboardMarkup.from_button(
|
||||
InlineKeyboardButton(text="Continue here!", url=url)
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""This is a very simple example on how one could implement a custom error handler."""
|
||||
@@ -40,7 +40,7 @@ async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> N
|
||||
# You might need to add some logic to deal with messages longer than the 4096 character limit.
|
||||
update_str = update.to_dict() if isinstance(update, Update) else str(update)
|
||||
message = (
|
||||
f"An exception was raised while handling an update\n"
|
||||
"An exception was raised while handling an update\n"
|
||||
f"<pre>update = {html.escape(json.dumps(update_str, indent=2, ensure_ascii=False))}"
|
||||
"</pre>\n\n"
|
||||
f"<pre>context.chat_data = {html.escape(str(context.chat_data))}</pre>\n\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""Simple inline keyboard bot with multiple CallbackQueryHandlers.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""Basic example for a bot that can receive payment from user."""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -59,7 +59,7 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
if context.user_data:
|
||||
reply_text += (
|
||||
f" You already told me your {', '.join(context.user_data.keys())}. Why don't you "
|
||||
f"tell me something more about yourself? Or change anything I already know."
|
||||
"tell me something more about yourself? Or change anything I already know."
|
||||
)
|
||||
else:
|
||||
reply_text += (
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=import-error
|
||||
"""Simple Bot to reply to Telegram messages.
|
||||
|
||||
This is built on the API wrapper, see echobot.py to see the same example built
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument, import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=unused-argument,import-error
|
||||
# pylint: disable=unused-argument
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -45,8 +45,10 @@ async def web_app_data(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
|
||||
# (see webappbot.html)
|
||||
data = json.loads(update.effective_message.web_app_data.data)
|
||||
await update.message.reply_html(
|
||||
text=f"You selected the color with the HEX value <code>{data['hex']}</code>. The "
|
||||
f"corresponding RGB value is <code>{tuple(data['rgb'].values())}</code>.",
|
||||
text=(
|
||||
f"You selected the color with the HEX value <code>{data['hex']}</code>. The "
|
||||
f"corresponding RGB value is <code>{tuple(data['rgb'].values())}</code>."
|
||||
),
|
||||
reply_markup=ReplyKeyboardRemove(),
|
||||
)
|
||||
|
||||
|
||||
+2
-3
@@ -12,10 +12,9 @@ target-version = "py38"
|
||||
show-fixes = true
|
||||
ignore = ["PLR2004", "PLR0911", "PLR0912", "PLR0913", "PLR0915", "PERF203"]
|
||||
select = ["E", "F", "I", "PL", "UP", "RUF", "PTH", "C4", "B", "PIE", "SIM", "RET", "RSE",
|
||||
"G", "ISC", "PT", "ASYNC", "TCH", "CPY", "SLOT", "PERF", "PYI"]
|
||||
"G", "ISC", "PT", "ASYNC", "TCH", "SLOT", "PERF", "PYI", "FLY", "AIR"]
|
||||
# Add "FURB" after it's out of preview
|
||||
|
||||
[tool.ruff.per-file-ignores]
|
||||
"tests/*.py" = ["B018"]
|
||||
"**/__init__.py" = ["CPY001"]
|
||||
"examples/**.py" = ["CPY001"]
|
||||
"tests/**.py" = ["RUF012"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
pre-commit # needed for pre-commit hooks in the git commit command
|
||||
|
||||
# For the test suite
|
||||
pytest==7.4.0
|
||||
pytest==7.4.2
|
||||
pytest-asyncio==0.21.1 # needed because pytest doesn't come with native support for coroutines as tests
|
||||
pytest-xdist==3.3.1 # xdist runs tests in parallel
|
||||
flaky # Used for flaky tests (flaky decorator)
|
||||
|
||||
@@ -16,7 +16,7 @@ cryptography!=3.4,!=3.4.1,!=3.4.2,!=3.4.3,>=39.0.1 # passport
|
||||
aiolimiter~=1.1.0 # rate-limiter!ext
|
||||
|
||||
# tornado is rather stable, but let's not allow the next mayor release without prior testing
|
||||
tornado~=6.2 # webhooks!ext
|
||||
tornado~=6.3.3 # webhooks!ext
|
||||
|
||||
# Cachetools and APS don't have a strict stability policy.
|
||||
# Let's be cautious for now.
|
||||
|
||||
+1
-1
@@ -6,4 +6,4 @@
|
||||
# versions and only increase the lower bound if necessary
|
||||
|
||||
# httpx has no stable release yet, so let's be cautious for now
|
||||
httpx ~= 0.24.1
|
||||
httpx ~= 0.25.0
|
||||
|
||||
@@ -19,7 +19,7 @@ exclude = setup.py, setup-raw.py docs/source/conf.py
|
||||
disable = duplicate-code,too-many-arguments,too-many-public-methods,too-few-public-methods,
|
||||
broad-except,too-many-instance-attributes,fixme,missing-function-docstring,
|
||||
missing-class-docstring,too-many-locals,too-many-lines,too-many-branches,
|
||||
too-many-statements
|
||||
too-many-statements, cyclic-import
|
||||
enable=useless-suppression ; Warns about unused pylint ignores
|
||||
exclude-protected=_unfrozen
|
||||
|
||||
|
||||
+211
-181
@@ -69,7 +69,6 @@ from telegram._files.contact import Contact
|
||||
from telegram._files.document import Document
|
||||
from telegram._files.file import File
|
||||
from telegram._files.inputmedia import InputMedia
|
||||
from telegram._files.inputsticker import InputSticker
|
||||
from telegram._files.location import Location
|
||||
from telegram._files.photosize import PhotoSize
|
||||
from telegram._files.sticker import MaskPosition, Sticker, StickerSet
|
||||
@@ -79,13 +78,10 @@ from telegram._files.videonote import VideoNote
|
||||
from telegram._files.voice import Voice
|
||||
from telegram._forumtopic import ForumTopic
|
||||
from telegram._games.gamehighscore import GameHighScore
|
||||
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
|
||||
from telegram._inline.inlinequeryresultsbutton import InlineQueryResultsButton
|
||||
from telegram._menubutton import MenuButton
|
||||
from telegram._message import Message
|
||||
from telegram._messageid import MessageId
|
||||
from telegram._passport.passportelementerrors import PassportElementError
|
||||
from telegram._payment.shippingoption import ShippingOption
|
||||
from telegram._poll import Poll
|
||||
from telegram._sentwebappmessage import SentWebAppMessage
|
||||
from telegram._telegramobject import TelegramObject
|
||||
@@ -96,6 +92,7 @@ from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue
|
||||
from telegram._utils.files import is_local_file, parse_file_input
|
||||
from telegram._utils.logging import get_logger
|
||||
from telegram._utils.repr import build_repr_with_selected_attrs
|
||||
from telegram._utils.types import (
|
||||
CorrectOptionID,
|
||||
DVInput,
|
||||
@@ -115,14 +112,18 @@ from telegram.warnings import PTBUserWarning
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import (
|
||||
InlineKeyboardMarkup,
|
||||
InlineQueryResult,
|
||||
InputFile,
|
||||
InputMediaAudio,
|
||||
InputMediaDocument,
|
||||
InputMediaPhoto,
|
||||
InputMediaVideo,
|
||||
InputSticker,
|
||||
LabeledPrice,
|
||||
MessageEntity,
|
||||
PassportElementError,
|
||||
ShippingOption,
|
||||
)
|
||||
|
||||
BT = TypeVar("BT", bound="Bot")
|
||||
@@ -290,8 +291,8 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
if warning_string:
|
||||
self._warn(
|
||||
f"You set the HTTP version for the {warning_string} HTTPXRequest instance to "
|
||||
f"HTTP/2. The self hosted bot api instances only support HTTP/1.1. You should "
|
||||
f"either run a HTTP proxy in front of it which supports HTTP/2 or use HTTP/1.1.",
|
||||
"HTTP/2. The self hosted bot api instances only support HTTP/1.1. You should "
|
||||
"either run a HTTP proxy in front of it which supports HTTP/2 or use HTTP/1.1.",
|
||||
PTBUserWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
@@ -308,6 +309,65 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
|
||||
self._freeze()
|
||||
|
||||
async def __aenter__(self: BT) -> BT:
|
||||
try:
|
||||
await self.initialize()
|
||||
return self
|
||||
except Exception as exc:
|
||||
await self.shutdown()
|
||||
raise exc
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
# Make sure not to return `True` so that exceptions are not suppressed
|
||||
# https://docs.python.org/3/reference/datamodel.html?#object.__aexit__
|
||||
await self.shutdown()
|
||||
|
||||
def __reduce__(self) -> NoReturn:
|
||||
"""Customizes how :func:`copy.deepcopy` processes objects of this type. Bots can not
|
||||
be pickled and this method will always raise an exception.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
Raises:
|
||||
:exc:`pickle.PicklingError`
|
||||
"""
|
||||
raise pickle.PicklingError("Bot objects cannot be pickled!")
|
||||
|
||||
def __deepcopy__(self, memodict: Dict[int, object]) -> NoReturn:
|
||||
"""Customizes how :func:`copy.deepcopy` processes objects of this type. Bots can not
|
||||
be deepcopied and this method will always raise an exception.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
Raises:
|
||||
:exc:`TypeError`
|
||||
"""
|
||||
raise TypeError("Bot objects cannot be deepcopied!")
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, self.__class__):
|
||||
return self.bot == other.bot
|
||||
return False
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.__class__, self.bot))
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Give a string representation of the bot in the form ``Bot[token=...]``.
|
||||
|
||||
As this class doesn't implement :meth:`object.__str__`, the default implementation
|
||||
will be used, which is equivalent to :meth:`__repr__`.
|
||||
|
||||
Returns:
|
||||
:obj:`str`
|
||||
"""
|
||||
return build_repr_with_selected_attrs(self, token=self.token)
|
||||
|
||||
@property
|
||||
def token(self) -> str:
|
||||
""":obj:`str`: Bot's unique authentication token.
|
||||
@@ -353,6 +413,93 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
"""
|
||||
return self._private_key
|
||||
|
||||
@property
|
||||
def request(self) -> BaseRequest:
|
||||
"""The :class:`~telegram.request.BaseRequest` object used by this bot.
|
||||
|
||||
Warning:
|
||||
Requests to the Bot API are made by the various methods of this class. This attribute
|
||||
should *not* be used manually.
|
||||
"""
|
||||
return self._request[1]
|
||||
|
||||
@property
|
||||
def bot(self) -> User:
|
||||
""":class:`telegram.User`: User instance for the bot as returned by :meth:`get_me`.
|
||||
|
||||
Warning:
|
||||
This value is the cached return value of :meth:`get_me`. If the bots profile is
|
||||
changed during runtime, this value won't reflect the changes until :meth:`get_me` is
|
||||
called again.
|
||||
|
||||
.. seealso:: :meth:`initialize`
|
||||
"""
|
||||
if self._bot_user is None:
|
||||
raise RuntimeError(
|
||||
f"{self.__class__.__name__} is not properly initialized. Call "
|
||||
f"`{self.__class__.__name__}.initialize` before accessing this property."
|
||||
)
|
||||
return self._bot_user
|
||||
|
||||
@property
|
||||
def id(self) -> int:
|
||||
""":obj:`int`: Unique identifier for this bot. Shortcut for the corresponding attribute of
|
||||
:attr:`bot`.
|
||||
"""
|
||||
return self.bot.id
|
||||
|
||||
@property
|
||||
def first_name(self) -> str:
|
||||
""":obj:`str`: Bot's first name. Shortcut for the corresponding attribute of
|
||||
:attr:`bot`.
|
||||
"""
|
||||
return self.bot.first_name
|
||||
|
||||
@property
|
||||
def last_name(self) -> str:
|
||||
""":obj:`str`: Optional. Bot's last name. Shortcut for the corresponding attribute of
|
||||
:attr:`bot`.
|
||||
"""
|
||||
return self.bot.last_name # type: ignore
|
||||
|
||||
@property
|
||||
def username(self) -> str:
|
||||
""":obj:`str`: Bot's username. Shortcut for the corresponding attribute of
|
||||
:attr:`bot`.
|
||||
"""
|
||||
return self.bot.username # type: ignore
|
||||
|
||||
@property
|
||||
def link(self) -> str:
|
||||
""":obj:`str`: Convenience property. Returns the t.me link of the bot."""
|
||||
return f"https://t.me/{self.username}"
|
||||
|
||||
@property
|
||||
def can_join_groups(self) -> bool:
|
||||
""":obj:`bool`: Bot's :attr:`telegram.User.can_join_groups` attribute. Shortcut for the
|
||||
corresponding attribute of :attr:`bot`.
|
||||
"""
|
||||
return self.bot.can_join_groups # type: ignore
|
||||
|
||||
@property
|
||||
def can_read_all_group_messages(self) -> bool:
|
||||
""":obj:`bool`: Bot's :attr:`telegram.User.can_read_all_group_messages` attribute.
|
||||
Shortcut for the corresponding attribute of :attr:`bot`.
|
||||
"""
|
||||
return self.bot.can_read_all_group_messages # type: ignore
|
||||
|
||||
@property
|
||||
def supports_inline_queries(self) -> bool:
|
||||
""":obj:`bool`: Bot's :attr:`telegram.User.supports_inline_queries` attribute.
|
||||
Shortcut for the corresponding attribute of :attr:`bot`.
|
||||
"""
|
||||
return self.bot.supports_inline_queries # type: ignore
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
""":obj:`str`: Bot's @username. Shortcut for the corresponding attribute of :attr:`bot`."""
|
||||
return f"@{self.username}"
|
||||
|
||||
@classmethod
|
||||
def _warn(
|
||||
cls, message: str, category: Type[Warning] = PTBUserWarning, stacklevel: int = 0
|
||||
@@ -362,28 +509,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
"""
|
||||
warn(message=message, category=category, stacklevel=stacklevel + 1)
|
||||
|
||||
def __reduce__(self) -> NoReturn:
|
||||
"""Customizes how :func:`copy.deepcopy` processes objects of this type. Bots can not
|
||||
be pickled and this method will always raise an exception.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
Raises:
|
||||
:exc:`pickle.PicklingError`
|
||||
"""
|
||||
raise pickle.PicklingError("Bot objects cannot be pickled!")
|
||||
|
||||
def __deepcopy__(self, memodict: Dict[int, object]) -> NoReturn:
|
||||
"""Customizes how :func:`copy.deepcopy` processes objects of this type. Bots can not
|
||||
be deepcopied and this method will always raise an exception.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
Raises:
|
||||
:exc:`TypeError`
|
||||
"""
|
||||
raise TypeError("Bot objects cannot be deepcopied!")
|
||||
|
||||
# TODO: After https://youtrack.jetbrains.com/issue/PY-50952 is fixed, we can revisit this and
|
||||
# consider adding Paramspec from typing_extensions to properly fix this. Currently a workaround
|
||||
def _log(func: Any): # type: ignore[no-untyped-def] # skipcq: PY-D0003
|
||||
@@ -621,111 +746,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
await asyncio.gather(self._request[0].shutdown(), self._request[1].shutdown())
|
||||
self._initialized = False
|
||||
|
||||
async def __aenter__(self: BT) -> BT:
|
||||
try:
|
||||
await self.initialize()
|
||||
return self
|
||||
except Exception as exc:
|
||||
await self.shutdown()
|
||||
raise exc
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
# Make sure not to return `True` so that exceptions are not suppressed
|
||||
# https://docs.python.org/3/reference/datamodel.html?#object.__aexit__
|
||||
await self.shutdown()
|
||||
|
||||
@property
|
||||
def request(self) -> BaseRequest:
|
||||
"""The :class:`~telegram.request.BaseRequest` object used by this bot.
|
||||
|
||||
Warning:
|
||||
Requests to the Bot API are made by the various methods of this class. This attribute
|
||||
should *not* be used manually.
|
||||
"""
|
||||
return self._request[1]
|
||||
|
||||
@property
|
||||
def bot(self) -> User:
|
||||
""":class:`telegram.User`: User instance for the bot as returned by :meth:`get_me`.
|
||||
|
||||
Warning:
|
||||
This value is the cached return value of :meth:`get_me`. If the bots profile is
|
||||
changed during runtime, this value won't reflect the changes until :meth:`get_me` is
|
||||
called again.
|
||||
|
||||
.. seealso:: :meth:`initialize`
|
||||
"""
|
||||
if self._bot_user is None:
|
||||
raise RuntimeError(
|
||||
f"{self.__class__.__name__} is not properly initialized. Call "
|
||||
f"`{self.__class__.__name__}.initialize` before accessing this property."
|
||||
)
|
||||
return self._bot_user
|
||||
|
||||
@property
|
||||
def id(self) -> int: # pylint: disable=invalid-name
|
||||
""":obj:`int`: Unique identifier for this bot. Shortcut for the corresponding attribute of
|
||||
:attr:`bot`.
|
||||
"""
|
||||
return self.bot.id
|
||||
|
||||
@property
|
||||
def first_name(self) -> str:
|
||||
""":obj:`str`: Bot's first name. Shortcut for the corresponding attribute of
|
||||
:attr:`bot`.
|
||||
"""
|
||||
return self.bot.first_name
|
||||
|
||||
@property
|
||||
def last_name(self) -> str:
|
||||
""":obj:`str`: Optional. Bot's last name. Shortcut for the corresponding attribute of
|
||||
:attr:`bot`.
|
||||
"""
|
||||
return self.bot.last_name # type: ignore
|
||||
|
||||
@property
|
||||
def username(self) -> str:
|
||||
""":obj:`str`: Bot's username. Shortcut for the corresponding attribute of
|
||||
:attr:`bot`.
|
||||
"""
|
||||
return self.bot.username # type: ignore
|
||||
|
||||
@property
|
||||
def link(self) -> str:
|
||||
""":obj:`str`: Convenience property. Returns the t.me link of the bot."""
|
||||
return f"https://t.me/{self.username}"
|
||||
|
||||
@property
|
||||
def can_join_groups(self) -> bool:
|
||||
""":obj:`bool`: Bot's :attr:`telegram.User.can_join_groups` attribute. Shortcut for the
|
||||
corresponding attribute of :attr:`bot`.
|
||||
"""
|
||||
return self.bot.can_join_groups # type: ignore
|
||||
|
||||
@property
|
||||
def can_read_all_group_messages(self) -> bool:
|
||||
""":obj:`bool`: Bot's :attr:`telegram.User.can_read_all_group_messages` attribute.
|
||||
Shortcut for the corresponding attribute of :attr:`bot`.
|
||||
"""
|
||||
return self.bot.can_read_all_group_messages # type: ignore
|
||||
|
||||
@property
|
||||
def supports_inline_queries(self) -> bool:
|
||||
""":obj:`bool`: Bot's :attr:`telegram.User.supports_inline_queries` attribute.
|
||||
Shortcut for the corresponding attribute of :attr:`bot`.
|
||||
"""
|
||||
return self.bot.supports_inline_queries # type: ignore
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
""":obj:`str`: Bot's @username. Shortcut for the corresponding attribute of :attr:`bot`."""
|
||||
return f"@{self.username}"
|
||||
|
||||
@_log
|
||||
async def get_me(
|
||||
self,
|
||||
@@ -2154,7 +2174,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
inline_message_id: Optional[str] = None,
|
||||
latitude: Optional[float] = None,
|
||||
longitude: Optional[float] = None,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
horizontal_accuracy: Optional[float] = None,
|
||||
heading: Optional[int] = None,
|
||||
proximity_alert_radius: Optional[int] = None,
|
||||
@@ -2247,7 +2267,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
chat_id: Optional[Union[str, int]] = None,
|
||||
message_id: Optional[int] = None,
|
||||
inline_message_id: Optional[str] = None,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2521,11 +2541,11 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
@_log
|
||||
async def send_game(
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
chat_id: int,
|
||||
game_short_name: str,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
@@ -2539,7 +2559,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
"""Use this method to send a game.
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat.
|
||||
chat_id (:obj:`int`): Unique identifier for the target chat.
|
||||
game_short_name (:obj:`str`): Short name of the game, serves as the unique identifier
|
||||
for the game. Set up your games via `@BotFather <https://t.me/BotFather>`_.
|
||||
disable_notification (:obj:`bool`, optional): |disable_notification|
|
||||
@@ -2826,7 +2846,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
@_log
|
||||
async def get_user_profile_photos(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
offset: Optional[int] = None,
|
||||
limit: Optional[int] = None,
|
||||
*,
|
||||
@@ -2938,7 +2958,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
async def ban_chat_member(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
until_date: Optional[Union[int, datetime]] = None,
|
||||
revoke_messages: Optional[bool] = None,
|
||||
*,
|
||||
@@ -3046,7 +3066,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
async def unban_chat_member(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
only_if_banned: Optional[bool] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -3203,7 +3223,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
inline_message_id: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -3279,7 +3299,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
message_id: Optional[int] = None,
|
||||
inline_message_id: Optional[str] = None,
|
||||
caption: Optional[str] = None,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
*,
|
||||
@@ -3349,7 +3369,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
chat_id: Optional[Union[str, int]] = None,
|
||||
message_id: Optional[int] = None,
|
||||
inline_message_id: Optional[str] = None,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -3876,7 +3896,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
async def get_chat_member(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -4011,9 +4031,9 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
@_log
|
||||
async def set_game_score(
|
||||
self,
|
||||
user_id: Union[int, str],
|
||||
user_id: int,
|
||||
score: int,
|
||||
chat_id: Optional[Union[str, int]] = None,
|
||||
chat_id: Optional[int] = None,
|
||||
message_id: Optional[int] = None,
|
||||
inline_message_id: Optional[str] = None,
|
||||
force: Optional[bool] = None,
|
||||
@@ -4037,7 +4057,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
decrease. This can be useful when fixing mistakes or banning cheaters.
|
||||
disable_edit_message (:obj:`bool`, optional): Pass :obj:`True`, if the game message
|
||||
should not be automatically edited to include the current scoreboard.
|
||||
chat_id (:obj:`int` | :obj:`str`, optional): Required if :paramref:`inline_message_id`
|
||||
chat_id (:obj:`int`, optional): Required if :paramref:`inline_message_id`
|
||||
is not specified. Unique identifier for the target chat.
|
||||
message_id (:obj:`int`, optional): Required if :paramref:`inline_message_id` is not
|
||||
specified. Identifier of the sent message.
|
||||
@@ -4076,8 +4096,8 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
@_log
|
||||
async def get_game_high_scores(
|
||||
self,
|
||||
user_id: Union[int, str],
|
||||
chat_id: Optional[Union[str, int]] = None,
|
||||
user_id: int,
|
||||
chat_id: Optional[int] = None,
|
||||
message_id: Optional[int] = None,
|
||||
inline_message_id: Optional[str] = None,
|
||||
*,
|
||||
@@ -4101,7 +4121,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
|
||||
Args:
|
||||
user_id (:obj:`int`): Target user id.
|
||||
chat_id (:obj:`int` | :obj:`str`, optional): Required if :paramref:`inline_message_id`
|
||||
chat_id (:obj:`int`, optional): Required if :paramref:`inline_message_id`
|
||||
is not specified. Unique identifier for the target chat.
|
||||
message_id (:obj:`int`, optional): Required if :paramref:`inline_message_id` is not
|
||||
specified. Identifier of the sent message.
|
||||
@@ -4156,7 +4176,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
is_flexible: Optional[bool] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
provider_data: Optional[Union[str, object]] = None,
|
||||
send_phone_number_to_provider: Optional[bool] = None,
|
||||
send_email_to_provider: Optional[bool] = None,
|
||||
@@ -4317,11 +4337,11 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
)
|
||||
|
||||
@_log
|
||||
async def answer_shipping_query( # pylint: disable=invalid-name
|
||||
async def answer_shipping_query(
|
||||
self,
|
||||
shipping_query_id: str,
|
||||
ok: bool,
|
||||
shipping_options: Optional[Sequence[ShippingOption]] = None,
|
||||
shipping_options: Optional[Sequence["ShippingOption"]] = None,
|
||||
error_message: Optional[str] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -4376,7 +4396,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
)
|
||||
|
||||
@_log
|
||||
async def answer_pre_checkout_query( # pylint: disable=invalid-name
|
||||
async def answer_pre_checkout_query(
|
||||
self,
|
||||
pre_checkout_query_id: str,
|
||||
ok: bool,
|
||||
@@ -4483,7 +4503,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
async def restrict_chat_member(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
permissions: ChatPermissions,
|
||||
until_date: Optional[Union[int, datetime]] = None,
|
||||
use_independent_chat_permissions: Optional[bool] = None,
|
||||
@@ -4557,7 +4577,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
async def promote_chat_member(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
can_change_info: Optional[bool] = None,
|
||||
can_post_messages: Optional[bool] = None,
|
||||
can_edit_messages: Optional[bool] = None,
|
||||
@@ -4570,6 +4590,9 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
can_manage_chat: Optional[bool] = None,
|
||||
can_manage_video_chats: Optional[bool] = None,
|
||||
can_manage_topics: Optional[bool] = None,
|
||||
can_post_stories: Optional[bool] = None,
|
||||
can_edit_stories: Optional[bool] = None,
|
||||
can_delete_stories: Optional[bool] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -4591,10 +4614,10 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
user_id (:obj:`int`): Unique identifier of the target user.
|
||||
is_anonymous (:obj:`bool`, optional): Pass :obj:`True`, if the administrator's presence
|
||||
in the chat is hidden.
|
||||
can_manage_chat (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
access the chat event log, chat statistics, message statistics in channels, see
|
||||
channel members, see anonymous administrators in supergroups and ignore slow mode.
|
||||
Implied by any other administrator privilege.
|
||||
can_manage_chat (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
access the chat event log, chat statistics, boost list in channels, see channel
|
||||
members, report spam messages, see anonymous administrators in supergroups and
|
||||
ignore slow mode. Implied by any other administrator privilege.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
@@ -4606,7 +4629,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
can_change_info (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
change chat title, photo and other settings.
|
||||
can_post_messages (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
create channel posts, channels only.
|
||||
post messages in the channel, or access channel statistics; channels only.
|
||||
can_edit_messages (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
edit messages of other users and can pin messages, channels only.
|
||||
can_delete_messages (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
@@ -4614,7 +4637,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
can_invite_users (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
invite new users to the chat.
|
||||
can_restrict_members (:obj:`bool`, optional): Pass :obj:`True`, if the administrator
|
||||
can restrict, ban or unban chat members.
|
||||
can restrict, ban or unban chat members, or access supergroup statistics.
|
||||
can_pin_messages (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
pin messages, supergroups only.
|
||||
can_promote_members (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
@@ -4625,6 +4648,18 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
allowed to create, rename, close, and reopen forum topics; supergroups only.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
can_post_stories (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
post stories in the channel; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_edit_stories (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
edit stories posted by other users; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_delete_stories (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
delete stories posted by other users; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
@@ -4648,6 +4683,9 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
"can_manage_chat": can_manage_chat,
|
||||
"can_manage_video_chats": can_manage_video_chats,
|
||||
"can_manage_topics": can_manage_topics,
|
||||
"can_post_stories": can_post_stories,
|
||||
"can_edit_stories": can_edit_stories,
|
||||
"can_delete_stories": can_delete_stories,
|
||||
}
|
||||
|
||||
return await self._post(
|
||||
@@ -4723,7 +4761,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
||||
async def set_chat_administrator_custom_title(
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
user_id: Union[int, str],
|
||||
user_id: int,
|
||||
custom_title: str,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -5478,7 +5516,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
@_log
|
||||
async def upload_sticker_file(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
sticker: Optional[FileInput],
|
||||
sticker_format: Optional[str],
|
||||
*,
|
||||
@@ -5538,9 +5576,9 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
@_log
|
||||
async def add_sticker_to_set(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
name: str,
|
||||
sticker: Optional[InputSticker],
|
||||
sticker: Optional["InputSticker"],
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
@@ -5636,10 +5674,10 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
@_log
|
||||
async def create_new_sticker_set(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
name: str,
|
||||
title: str,
|
||||
stickers: Optional[Sequence[InputSticker]],
|
||||
stickers: Optional[Sequence["InputSticker"]],
|
||||
sticker_format: Optional[str],
|
||||
sticker_type: Optional[str] = None,
|
||||
needs_repainting: Optional[bool] = None,
|
||||
@@ -5807,7 +5845,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
async def set_sticker_set_thumbnail(
|
||||
self,
|
||||
name: str,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
thumbnail: Optional[FileInput] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -6079,8 +6117,8 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
@_log
|
||||
async def set_passport_data_errors(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
errors: Sequence[PassportElementError],
|
||||
user_id: int,
|
||||
errors: Sequence["PassportElementError"],
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -6262,7 +6300,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
message_id: int,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -7843,14 +7881,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
|
||||
return data
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, self.__class__):
|
||||
return self.bot == other.bot
|
||||
return False
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.__class__, self.bot))
|
||||
|
||||
# camelCase aliases
|
||||
getMe = get_me
|
||||
"""Alias for :meth:`get_me`"""
|
||||
|
||||
@@ -127,7 +127,7 @@ class CallbackQuery(TelegramObject):
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
# Required
|
||||
self.id: str = id # pylint: disable=invalid-name
|
||||
self.id: str = id
|
||||
self.from_user: User = from_user
|
||||
self.chat_instance: str = chat_instance
|
||||
# Optionals
|
||||
@@ -531,7 +531,7 @@ class CallbackQuery(TelegramObject):
|
||||
|
||||
async def set_game_score(
|
||||
self,
|
||||
user_id: Union[int, str],
|
||||
user_id: int,
|
||||
score: int,
|
||||
force: Optional[bool] = None,
|
||||
disable_edit_message: Optional[bool] = None,
|
||||
@@ -589,7 +589,7 @@ class CallbackQuery(TelegramObject):
|
||||
|
||||
async def get_game_high_scores(
|
||||
self,
|
||||
user_id: Union[int, str],
|
||||
user_id: int,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
||||
+15
-6
@@ -374,7 +374,7 @@ class Chat(TelegramObject):
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
# Required
|
||||
self.id: int = id # pylint: disable=invalid-name
|
||||
self.id: int = id
|
||||
self.type: str = enum.get_member(constants.ChatType, type, type)
|
||||
# Optionals
|
||||
self.title: Optional[str] = title
|
||||
@@ -677,7 +677,7 @@ class Chat(TelegramObject):
|
||||
|
||||
async def get_member(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -707,7 +707,7 @@ class Chat(TelegramObject):
|
||||
|
||||
async def ban_member(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
revoke_messages: Optional[bool] = None,
|
||||
until_date: Optional[Union[int, datetime]] = None,
|
||||
*,
|
||||
@@ -877,7 +877,7 @@ class Chat(TelegramObject):
|
||||
|
||||
async def unban_member(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
only_if_banned: Optional[bool] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -909,7 +909,7 @@ class Chat(TelegramObject):
|
||||
|
||||
async def promote_member(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
can_change_info: Optional[bool] = None,
|
||||
can_post_messages: Optional[bool] = None,
|
||||
can_edit_messages: Optional[bool] = None,
|
||||
@@ -922,6 +922,9 @@ class Chat(TelegramObject):
|
||||
can_manage_chat: Optional[bool] = None,
|
||||
can_manage_video_chats: Optional[bool] = None,
|
||||
can_manage_topics: Optional[bool] = None,
|
||||
can_post_stories: Optional[bool] = None,
|
||||
can_edit_stories: Optional[bool] = None,
|
||||
can_delete_stories: Optional[bool] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -941,6 +944,9 @@ class Chat(TelegramObject):
|
||||
The argument ``can_manage_voice_chats`` was renamed to
|
||||
:paramref:`~telegram.Bot.promote_chat_member.can_manage_video_chats` in accordance to
|
||||
Bot API 6.0.
|
||||
.. versionchanged:: 20.6
|
||||
The arguments `can_post_stories`, `can_edit_stories` and `can_delete_stories` were
|
||||
added.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
@@ -966,11 +972,14 @@ class Chat(TelegramObject):
|
||||
can_manage_chat=can_manage_chat,
|
||||
can_manage_video_chats=can_manage_video_chats,
|
||||
can_manage_topics=can_manage_topics,
|
||||
can_post_stories=can_post_stories,
|
||||
can_edit_stories=can_edit_stories,
|
||||
can_delete_stories=can_delete_stories,
|
||||
)
|
||||
|
||||
async def restrict_member(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
permissions: ChatPermissions,
|
||||
until_date: Optional[Union[int, datetime]] = None,
|
||||
use_independent_chat_permissions: Optional[bool] = None,
|
||||
|
||||
@@ -31,26 +31,31 @@ class ChatAdministratorRights(TelegramObject):
|
||||
:attr:`can_delete_messages`, :attr:`can_manage_video_chats`, :attr:`can_restrict_members`,
|
||||
:attr:`can_promote_members`, :attr:`can_change_info`, :attr:`can_invite_users`,
|
||||
:attr:`can_post_messages`, :attr:`can_edit_messages`, :attr:`can_pin_messages`,
|
||||
:attr:`can_manage_topics` are equal.
|
||||
:attr:`can_manage_topics`, :attr:`can_post_stories`, :attr:`can_delete_stories`, and
|
||||
:attr:`can_edit_stories` are equal.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
:attr:`can_manage_topics` is considered as well when comparing objects of
|
||||
this type in terms of equality.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
.. versionchanged:: 20.6
|
||||
:attr:`can_post_stories`, :attr:`can_edit_stories`, and :attr:`can_delete_stories` are
|
||||
considered as well when comparing objects of this type in terms of equality.
|
||||
|
||||
Args:
|
||||
is_anonymous (:obj:`bool`): :obj:`True`, if the user's presence in the chat is hidden.
|
||||
can_manage_chat (:obj:`bool`): :obj:`True`, if the administrator can access the chat event
|
||||
log, chat statistics, message statistics in channels, see channel members, see
|
||||
anonymous administrators in supergroups and ignore slow mode. Implied by any other
|
||||
administrator privilege.
|
||||
log, chat statistics, boost list in channels, see channel members, report spam
|
||||
messages, see anonymous administrators in supergroups and ignore slow mode.
|
||||
Implied by any other administrator privilege.
|
||||
can_delete_messages (:obj:`bool`): :obj:`True`, if the administrator can delete messages of
|
||||
other users.
|
||||
can_manage_video_chats (:obj:`bool`): :obj:`True`, if the administrator can manage video
|
||||
chats.
|
||||
can_restrict_members (:obj:`bool`): :obj:`True`, if the administrator can restrict, ban or
|
||||
unban chat members.
|
||||
unban chat members, or access supergroup statistics.
|
||||
can_promote_members (:obj:`bool`): :obj:`True`, if the administrator can add new
|
||||
administrators with a subset of their own privileges or demote administrators
|
||||
that they have promoted, directly or indirectly (promoted by administrators that
|
||||
@@ -60,11 +65,23 @@ class ChatAdministratorRights(TelegramObject):
|
||||
can_invite_users (:obj:`bool`): :obj:`True`, if the user is allowed to invite new users to
|
||||
the chat.
|
||||
can_post_messages (:obj:`bool`, optional): :obj:`True`, if the administrator can post
|
||||
messages in the channel; channels only.
|
||||
messages in the channel, or access channel statistics; channels only.
|
||||
can_edit_messages (:obj:`bool`, optional): :obj:`True`, if the administrator can edit
|
||||
messages of other users.
|
||||
can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to pin
|
||||
messages; groups and supergroups only.
|
||||
can_post_stories (:obj:`bool`, optional): :obj:`True`, if the administrator can post
|
||||
stories in the channel; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_edit_stories (:obj:`bool`, optional): :obj:`True`, if the administrator can edit
|
||||
stories posted by other users; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_delete_stories (:obj:`bool`, optional): :obj:`True`, if the administrator can delete
|
||||
stories posted by other users; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_manage_topics (:obj:`bool`, optional): :obj:`True`, if the user is allowed
|
||||
to create, rename, close, and reopen forum topics; supergroups only.
|
||||
|
||||
@@ -73,15 +90,15 @@ class ChatAdministratorRights(TelegramObject):
|
||||
Attributes:
|
||||
is_anonymous (:obj:`bool`): :obj:`True`, if the user's presence in the chat is hidden.
|
||||
can_manage_chat (:obj:`bool`): :obj:`True`, if the administrator can access the chat event
|
||||
log, chat statistics, message statistics in channels, see channel members, see
|
||||
anonymous administrators in supergroups and ignore slow mode. Implied by any other
|
||||
administrator privilege.
|
||||
log, chat statistics, boost list in channels, see channel members, report spam
|
||||
messages, see anonymous administrators in supergroups and ignore slow mode.
|
||||
Implied by any other administrator privilege.
|
||||
can_delete_messages (:obj:`bool`): :obj:`True`, if the administrator can delete messages of
|
||||
other users.
|
||||
can_manage_video_chats (:obj:`bool`): :obj:`True`, if the administrator can manage video
|
||||
chats.
|
||||
can_restrict_members (:obj:`bool`): :obj:`True`, if the administrator can restrict, ban or
|
||||
unban chat members.
|
||||
unban chat members, or access supergroup statistics.
|
||||
can_promote_members (:obj:`bool`): :obj:`True`, if the administrator can add new
|
||||
administrators with a subset of their own privileges or demote administrators that he
|
||||
has promoted, directly or indirectly (promoted by administrators that were appointed by
|
||||
@@ -91,11 +108,23 @@ class ChatAdministratorRights(TelegramObject):
|
||||
can_invite_users (:obj:`bool`): :obj:`True`, if the user is allowed to invite new users to
|
||||
the chat.
|
||||
can_post_messages (:obj:`bool`): Optional. :obj:`True`, if the administrator can post
|
||||
messages in the channel; channels only.
|
||||
messages in the channel, or access channel statistics; channels only.
|
||||
can_edit_messages (:obj:`bool`): Optional. :obj:`True`, if the administrator can edit
|
||||
messages of other users.
|
||||
can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to pin
|
||||
messages; groups and supergroups only.
|
||||
can_post_stories (:obj:`bool`): Optional. :obj:`True`, if the administrator can post
|
||||
stories in the channel; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_edit_stories (:obj:`bool`): Optional. :obj:`True`, if the administrator can edit
|
||||
stories posted by other users; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_delete_stories (:obj:`bool`): Optional. :obj:`True`, if the administrator can delete
|
||||
stories posted by other users; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_manage_topics (:obj:`bool`): Optional. :obj:`True`, if the user is allowed
|
||||
to create, rename, close, and reopen forum topics; supergroups only.
|
||||
|
||||
@@ -115,6 +144,9 @@ class ChatAdministratorRights(TelegramObject):
|
||||
"can_edit_messages",
|
||||
"can_pin_messages",
|
||||
"can_manage_topics",
|
||||
"can_post_stories",
|
||||
"can_edit_stories",
|
||||
"can_delete_stories",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
@@ -131,6 +163,9 @@ class ChatAdministratorRights(TelegramObject):
|
||||
can_edit_messages: Optional[bool] = None,
|
||||
can_pin_messages: Optional[bool] = None,
|
||||
can_manage_topics: Optional[bool] = None,
|
||||
can_post_stories: Optional[bool] = None,
|
||||
can_edit_stories: Optional[bool] = None,
|
||||
can_delete_stories: Optional[bool] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
@@ -148,6 +183,9 @@ class ChatAdministratorRights(TelegramObject):
|
||||
self.can_post_messages: Optional[bool] = can_post_messages
|
||||
self.can_edit_messages: Optional[bool] = can_edit_messages
|
||||
self.can_pin_messages: Optional[bool] = can_pin_messages
|
||||
self.can_post_stories: Optional[bool] = can_post_stories
|
||||
self.can_edit_stories: Optional[bool] = can_edit_stories
|
||||
self.can_delete_stories: Optional[bool] = can_delete_stories
|
||||
self.can_manage_topics: Optional[bool] = can_manage_topics
|
||||
|
||||
self._id_attrs = (
|
||||
@@ -163,6 +201,9 @@ class ChatAdministratorRights(TelegramObject):
|
||||
self.can_edit_messages,
|
||||
self.can_pin_messages,
|
||||
self.can_manage_topics,
|
||||
self.can_post_stories,
|
||||
self.can_edit_stories,
|
||||
self.can_delete_stories,
|
||||
)
|
||||
|
||||
self._freeze()
|
||||
@@ -176,7 +217,7 @@ class ChatAdministratorRights(TelegramObject):
|
||||
|
||||
.. versionadded:: 20.0
|
||||
"""
|
||||
return cls(True, True, True, True, True, True, True, True, True, True, True, True)
|
||||
return cls(*(True,) * len(cls.__slots__))
|
||||
|
||||
@classmethod
|
||||
def no_rights(cls) -> "ChatAdministratorRights":
|
||||
@@ -186,6 +227,4 @@ class ChatAdministratorRights(TelegramObject):
|
||||
|
||||
.. versionadded:: 20.0
|
||||
"""
|
||||
return cls(
|
||||
False, False, False, False, False, False, False, False, False, False, False, False
|
||||
)
|
||||
return cls(*(False,) * len(cls.__slots__))
|
||||
|
||||
@@ -63,7 +63,7 @@ class ChatJoinRequest(TelegramObject):
|
||||
request. This number may have more than 32 significant bits and some programming
|
||||
languages may have difficulty/silent defects in interpreting it. But it has at most 52
|
||||
significant bits, so a 64-bit integer or double-precision float type are safe for
|
||||
storing this identifier. The bot can use this identifier for 24 hours to send messages
|
||||
storing this identifier. The bot can use this identifier for 5 minutes to send messages
|
||||
until the join request is processed, assuming no other administrator contacted the
|
||||
user.
|
||||
|
||||
|
||||
+42
-7
@@ -218,12 +218,25 @@ class ChatMemberAdministrator(ChatMember):
|
||||
can_invite_users (:obj:`bool`): :obj:`True`, if the user can invite
|
||||
new users to the chat.
|
||||
can_post_messages (:obj:`bool`, optional): :obj:`True`, if the
|
||||
administrator can post in the channel, channels only.
|
||||
administrator can post messages in the channel, or access channel statistics; channels
|
||||
only.
|
||||
can_edit_messages (:obj:`bool`, optional): :obj:`True`, if the
|
||||
administrator can edit messages of other users and can pin
|
||||
messages; channels only.
|
||||
can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed
|
||||
to pin messages; groups and supergroups only.
|
||||
can_post_stories (:obj:`bool`, optional): :obj:`True`, if the administrator can post
|
||||
stories in the channel; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_edit_stories (:obj:`bool`, optional): :obj:`True`, if the administrator can edit
|
||||
stories posted by other users; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_delete_stories (:obj:`bool`, optional): :obj:`True`, if the administrator can delete
|
||||
stories posted by other users; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_manage_topics (:obj:`bool`, optional): :obj:`True`, if the user is allowed
|
||||
to create, rename, close, and reopen forum topics; supergroups only.
|
||||
|
||||
@@ -238,10 +251,10 @@ class ChatMemberAdministrator(ChatMember):
|
||||
is allowed to edit administrator privileges of that user.
|
||||
is_anonymous (:obj:`bool`): :obj:`True`, if the user's
|
||||
presence in the chat is hidden.
|
||||
can_manage_chat (:obj:`bool`): :obj:`True`, if the administrator
|
||||
can access the chat event log, chat statistics, message statistics in
|
||||
channels, see channel members, see anonymous administrators in supergroups
|
||||
and ignore slow mode. Implied by any other administrator privilege.
|
||||
can_manage_chat (:obj:`bool`): :obj:`True`, if the administrator can access the chat event
|
||||
log, chat statistics, boost list in channels, see channel members, report spam
|
||||
messages, see anonymous administrators in supergroups and ignore slow mode.
|
||||
Implied by any other administrator privilege.
|
||||
can_delete_messages (:obj:`bool`): :obj:`True`, if the
|
||||
administrator can delete messages of other users.
|
||||
can_manage_video_chats (:obj:`bool`): :obj:`True`, if the
|
||||
@@ -249,7 +262,7 @@ class ChatMemberAdministrator(ChatMember):
|
||||
|
||||
.. versionadded:: 20.0
|
||||
can_restrict_members (:obj:`bool`): :obj:`True`, if the
|
||||
administrator can restrict, ban or unban chat members.
|
||||
administrator can restrict, ban or unban chat members, or access supergroup statistics.
|
||||
can_promote_members (:obj:`bool`): :obj:`True`, if the administrator can add new
|
||||
administrators with a subset of their own privileges or demote administrators
|
||||
that they have promoted, directly or indirectly (promoted by administrators that
|
||||
@@ -259,12 +272,25 @@ class ChatMemberAdministrator(ChatMember):
|
||||
can_invite_users (:obj:`bool`): :obj:`True`, if the user can invite
|
||||
new users to the chat.
|
||||
can_post_messages (:obj:`bool`): Optional. :obj:`True`, if the
|
||||
administrator can post in the channel, channels only.
|
||||
administrator can post messages in the channel or access channel statistics;
|
||||
channels only.
|
||||
can_edit_messages (:obj:`bool`): Optional. :obj:`True`, if the
|
||||
administrator can edit messages of other users and can pin
|
||||
messages; channels only.
|
||||
can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed
|
||||
to pin messages; groups and supergroups only.
|
||||
can_post_stories (:obj:`bool`): Optional. :obj:`True`, if the administrator can post
|
||||
stories in the channel; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_edit_stories (:obj:`bool`): Optional. :obj:`True`, if the administrator can edit
|
||||
stories posted by other users; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_delete_stories (:obj:`bool`): Optional. :obj:`True`, if the administrator can delete
|
||||
stories posted by other users; channels only.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_manage_topics (:obj:`bool`): Optional. :obj:`True`, if the user is allowed
|
||||
to create, rename, close, and reopen forum topics; supergroups only
|
||||
|
||||
@@ -287,6 +313,9 @@ class ChatMemberAdministrator(ChatMember):
|
||||
"can_pin_messages",
|
||||
"can_manage_topics",
|
||||
"custom_title",
|
||||
"can_post_stories",
|
||||
"can_edit_stories",
|
||||
"can_delete_stories",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
@@ -306,6 +335,9 @@ class ChatMemberAdministrator(ChatMember):
|
||||
can_pin_messages: Optional[bool] = None,
|
||||
can_manage_topics: Optional[bool] = None,
|
||||
custom_title: Optional[str] = None,
|
||||
can_post_stories: Optional[bool] = None,
|
||||
can_edit_stories: Optional[bool] = None,
|
||||
can_delete_stories: Optional[bool] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
@@ -325,6 +357,9 @@ class ChatMemberAdministrator(ChatMember):
|
||||
self.can_pin_messages: Optional[bool] = can_pin_messages
|
||||
self.can_manage_topics: Optional[bool] = can_manage_topics
|
||||
self.custom_title: Optional[str] = custom_title
|
||||
self.can_post_stories: Optional[bool] = can_post_stories
|
||||
self.can_edit_stories: Optional[bool] = can_edit_stories
|
||||
self.can_delete_stories: Optional[bool] = can_delete_stories
|
||||
|
||||
|
||||
class ChatMemberMember(ChatMember):
|
||||
|
||||
@@ -111,7 +111,7 @@ class InlineQuery(TelegramObject):
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
# Required
|
||||
self.id: str = id # pylint: disable=invalid-name
|
||||
self.id: str = id
|
||||
self.from_user: User = from_user
|
||||
self.query: str = query
|
||||
self.offset: str = offset
|
||||
|
||||
@@ -60,7 +60,7 @@ class InlineQueryResult(TelegramObject):
|
||||
|
||||
# Required
|
||||
self.type: str = type
|
||||
self.id: str = str(id) # pylint: disable=invalid-name
|
||||
self.id: str = str(id)
|
||||
|
||||
self._id_attrs = (self.id,)
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class InlineQueryResultsButton(TelegramObject):
|
||||
`Web App <https://core.telegram.org/bots/webapps>`_ that will be launched when the
|
||||
user presses the button. The Web App will be able to switch back to the inline mode
|
||||
using the method
|
||||
`switchInlineQuery <https://core.telegram.org/bots/webapps#initializing-web-apps>`_
|
||||
`switchInlineQuery <https://core.telegram.org/bots/webapps#initializing-mini-apps>`_
|
||||
inside the Web App.
|
||||
start_parameter (:obj:`str`, optional): Deep-linking parameter for the
|
||||
:guilabel:`/start` message sent to the bot when user presses the switch button.
|
||||
|
||||
@@ -86,7 +86,7 @@ class LoginUrl(TelegramObject):
|
||||
def __init__(
|
||||
self,
|
||||
url: str,
|
||||
forward_text: Optional[bool] = None,
|
||||
forward_text: Optional[str] = None,
|
||||
bot_username: Optional[str] = None,
|
||||
request_write_access: Optional[bool] = None,
|
||||
*,
|
||||
@@ -96,7 +96,7 @@ class LoginUrl(TelegramObject):
|
||||
# Required
|
||||
self.url: str = url
|
||||
# Optional
|
||||
self.forward_text: Optional[bool] = forward_text
|
||||
self.forward_text: Optional[str] = forward_text
|
||||
self.bot_username: Optional[str] = bot_username
|
||||
self.request_write_access: Optional[bool] = request_write_access
|
||||
|
||||
|
||||
+13
-10
@@ -330,7 +330,10 @@ class Message(TelegramObject):
|
||||
|
||||
.. versionadded:: 20.0
|
||||
write_access_allowed (:class:`telegram.WriteAccessAllowed`, optional): Service message:
|
||||
the user allowed the bot added to the attachment menu to write messages.
|
||||
the user allowed the bot to write messages after adding it to the attachment or side
|
||||
menu, launching a Web App from a link, or accepting an explicit request from a Web App
|
||||
sent by the method
|
||||
`requestWriteAccess <https://core.telegram.org/bots/webapps#initializing-mini-apps>`_.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
has_media_spoiler (:obj:`bool`, optional): :obj:`True`, if the message media is covered
|
||||
@@ -854,7 +857,7 @@ class Message(TelegramObject):
|
||||
return self.chat.id
|
||||
|
||||
@property
|
||||
def id(self) -> int: # pylint: disable=invalid-name
|
||||
def id(self) -> int:
|
||||
"""
|
||||
:obj:`int`: Shortcut for :attr:`message_id`.
|
||||
|
||||
@@ -2504,7 +2507,7 @@ class Message(TelegramObject):
|
||||
text: str,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2550,7 +2553,7 @@ class Message(TelegramObject):
|
||||
async def edit_caption(
|
||||
self,
|
||||
caption: Optional[str] = None,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
*,
|
||||
@@ -2597,7 +2600,7 @@ class Message(TelegramObject):
|
||||
async def edit_media(
|
||||
self,
|
||||
media: "InputMedia",
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2681,7 +2684,7 @@ class Message(TelegramObject):
|
||||
self,
|
||||
latitude: Optional[float] = None,
|
||||
longitude: Optional[float] = None,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
horizontal_accuracy: Optional[float] = None,
|
||||
heading: Optional[int] = None,
|
||||
proximity_alert_radius: Optional[int] = None,
|
||||
@@ -2731,7 +2734,7 @@ class Message(TelegramObject):
|
||||
|
||||
async def stop_live_location(
|
||||
self,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2771,7 +2774,7 @@ class Message(TelegramObject):
|
||||
|
||||
async def set_game_score(
|
||||
self,
|
||||
user_id: Union[int, str],
|
||||
user_id: int,
|
||||
score: int,
|
||||
force: Optional[bool] = None,
|
||||
disable_edit_message: Optional[bool] = None,
|
||||
@@ -2816,7 +2819,7 @@ class Message(TelegramObject):
|
||||
|
||||
async def get_game_high_scores(
|
||||
self,
|
||||
user_id: Union[int, str],
|
||||
user_id: int,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2886,7 +2889,7 @@ class Message(TelegramObject):
|
||||
|
||||
async def stop_poll(
|
||||
self,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram EncryptedPassportElement."""
|
||||
from base64 import b64decode
|
||||
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
|
||||
from typing import TYPE_CHECKING, Optional, Sequence, Tuple, Union
|
||||
|
||||
from telegram._passport.credentials import decrypt_json
|
||||
from telegram._passport.data import IdDocumentData, PersonalDetails, ResidentialAddress
|
||||
@@ -54,7 +54,7 @@ class EncryptedPassportElement(TelegramObject):
|
||||
data (:class:`telegram.PersonalDetails` | :class:`telegram.IdDocumentData` | \
|
||||
:class:`telegram.ResidentialAddress` | :obj:`str`, optional):
|
||||
Decrypted or encrypted data, available for "personal_details", "passport",
|
||||
"driver_license", "identity_card", "identity_passport" and "address" types.
|
||||
"driver_license", "identity_card", "internal_passport" and "address" types.
|
||||
phone_number (:obj:`str`, optional): User's verified phone number, available only for
|
||||
"phone_number" type.
|
||||
email (:obj:`str`, optional): User's verified email address, available only for "email"
|
||||
@@ -96,7 +96,7 @@ class EncryptedPassportElement(TelegramObject):
|
||||
data (:class:`telegram.PersonalDetails` | :class:`telegram.IdDocumentData` | \
|
||||
:class:`telegram.ResidentialAddress` | :obj:`str`):
|
||||
Optional. Decrypted or encrypted data, available for "personal_details", "passport",
|
||||
"driver_license", "identity_card", "identity_passport" and "address" types.
|
||||
"driver_license", "identity_card", "internal_passport" and "address" types.
|
||||
phone_number (:obj:`str`): Optional. User's verified phone number, available only for
|
||||
"phone_number" type.
|
||||
email (:obj:`str`): Optional. User's verified email address, available only for "email"
|
||||
@@ -151,7 +151,7 @@ class EncryptedPassportElement(TelegramObject):
|
||||
self,
|
||||
type: str, # pylint: disable=redefined-builtin
|
||||
hash: str, # pylint: disable=redefined-builtin
|
||||
data: Optional[PersonalDetails] = None,
|
||||
data: Optional[Union[PersonalDetails, IdDocumentData, ResidentialAddress]] = None,
|
||||
phone_number: Optional[str] = None,
|
||||
email: Optional[str] = None,
|
||||
files: Optional[Sequence[PassportFile]] = None,
|
||||
@@ -168,7 +168,7 @@ class EncryptedPassportElement(TelegramObject):
|
||||
# Required
|
||||
self.type: str = type
|
||||
# Optionals
|
||||
self.data: Optional[PersonalDetails] = data
|
||||
self.data: Optional[Union[PersonalDetails, IdDocumentData, ResidentialAddress]] = data
|
||||
self.phone_number: Optional[str] = phone_number
|
||||
self.email: Optional[str] = email
|
||||
self.files: Tuple[PassportFile, ...] = parse_sequence_arg(files)
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
# pylint: disable=redefined-builtin
|
||||
"""This module contains the classes that represent Telegram PassportElementError."""
|
||||
|
||||
from typing import Optional
|
||||
from typing import List, Optional
|
||||
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
|
||||
|
||||
class PassportElementError(TelegramObject):
|
||||
@@ -173,23 +175,48 @@ class PassportElementErrorFiles(PassportElementError):
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of
|
||||
``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``,
|
||||
``"passport_registration"``, ``"temporary_registration"``.
|
||||
file_hashes (List[:obj:`str`]): List of base64-encoded file hashes.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("file_hashes",)
|
||||
__slots__ = ("_file_hashes",)
|
||||
|
||||
def __init__(
|
||||
self, type: str, file_hashes: str, message: str, *, api_kwargs: Optional[JSONDict] = None
|
||||
self,
|
||||
type: str,
|
||||
file_hashes: List[str],
|
||||
message: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
# Required
|
||||
super().__init__("files", type, message, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.file_hashes: str = file_hashes
|
||||
self._file_hashes: List[str] = file_hashes
|
||||
|
||||
self._id_attrs = (self.source, self.type, self.message, *tuple(file_hashes))
|
||||
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict` for details."""
|
||||
data = super().to_dict(recursive)
|
||||
data["file_hashes"] = self._file_hashes
|
||||
return data
|
||||
|
||||
@property
|
||||
def file_hashes(self) -> List[str]:
|
||||
"""List of base64-encoded file hashes.
|
||||
|
||||
.. deprecated:: 20.6
|
||||
This attribute will return a tuple instead of a list in future major versions.
|
||||
"""
|
||||
warn(
|
||||
"The attribute `file_hashes` will return a tuple instead of a list in future major"
|
||||
" versions.",
|
||||
PTBDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return self._file_hashes
|
||||
|
||||
|
||||
class PassportElementErrorFrontSide(PassportElementError):
|
||||
"""
|
||||
@@ -365,23 +392,49 @@ class PassportElementErrorTranslationFiles(PassportElementError):
|
||||
one of ``"passport"``, ``"driver_license"``, ``"identity_card"``,
|
||||
``"internal_passport"``, ``"utility_bill"``, ``"bank_statement"``,
|
||||
``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``.
|
||||
file_hashes (List[:obj:`str`]): List of base64-encoded file hashes.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("file_hashes",)
|
||||
__slots__ = ("_file_hashes",)
|
||||
|
||||
def __init__(
|
||||
self, type: str, file_hashes: str, message: str, *, api_kwargs: Optional[JSONDict] = None
|
||||
self,
|
||||
type: str,
|
||||
file_hashes: List[str],
|
||||
message: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
# Required
|
||||
super().__init__("translation_files", type, message, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.file_hashes: str = file_hashes
|
||||
self._file_hashes: List[str] = file_hashes
|
||||
|
||||
self._id_attrs = (self.source, self.type, self.message, *tuple(file_hashes))
|
||||
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict` for details."""
|
||||
data = super().to_dict(recursive)
|
||||
data["file_hashes"] = self._file_hashes
|
||||
return data
|
||||
|
||||
@property
|
||||
def file_hashes(self) -> List[str]:
|
||||
"""List of base64-encoded file hashes.
|
||||
|
||||
.. deprecated:: 20.6
|
||||
This attribute will return a tuple instead of a list in future major versions.
|
||||
"""
|
||||
warn(
|
||||
"The attribute `file_hashes` will return a tuple instead of a list in future major"
|
||||
" versions. See the stability policy:"
|
||||
" https://docs.python-telegram-bot.org/en/stable/stability_policy.html",
|
||||
PTBDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return self._file_hashes
|
||||
|
||||
|
||||
class PassportElementErrorUnspecified(PassportElementError):
|
||||
"""
|
||||
|
||||
@@ -23,6 +23,8 @@ from typing import TYPE_CHECKING, List, Optional, Tuple
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot, File, FileCredentials
|
||||
@@ -45,6 +47,10 @@ class PassportFile(TelegramObject):
|
||||
file_size (:obj:`int`): File size in bytes.
|
||||
file_date (:obj:`int`): Unix time when the file was uploaded.
|
||||
|
||||
.. deprecated:: 20.6
|
||||
This argument will only accept a datetime instead of an integer in future
|
||||
major versions.
|
||||
|
||||
Attributes:
|
||||
file_id (:obj:`str`): Identifier for this file, which can be used to download
|
||||
or reuse the file.
|
||||
@@ -52,13 +58,10 @@ class PassportFile(TelegramObject):
|
||||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
file_size (:obj:`int`): File size in bytes.
|
||||
file_date (:obj:`int`): Unix time when the file was uploaded.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"file_date",
|
||||
"_file_date",
|
||||
"file_id",
|
||||
"file_size",
|
||||
"_credentials",
|
||||
@@ -81,7 +84,7 @@ class PassportFile(TelegramObject):
|
||||
self.file_id: str = file_id
|
||||
self.file_unique_id: str = file_unique_id
|
||||
self.file_size: int = file_size
|
||||
self.file_date: int = file_date
|
||||
self._file_date: int = file_date
|
||||
# Optionals
|
||||
|
||||
self._credentials: Optional[FileCredentials] = credentials
|
||||
@@ -90,6 +93,27 @@ class PassportFile(TelegramObject):
|
||||
|
||||
self._freeze()
|
||||
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict` for details."""
|
||||
data = super().to_dict(recursive)
|
||||
data["file_date"] = self._file_date
|
||||
return data
|
||||
|
||||
@property
|
||||
def file_date(self) -> int:
|
||||
""":obj:`int`: Unix time when the file was uploaded.
|
||||
|
||||
.. deprecated:: 20.6
|
||||
This attribute will return a datetime instead of a integer in future major versions.
|
||||
"""
|
||||
warn(
|
||||
"The attribute `file_date` will return a datetime instead of an integer in future"
|
||||
" major versions.",
|
||||
PTBDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return self._file_date
|
||||
|
||||
@classmethod
|
||||
def de_json_decrypted(
|
||||
cls, data: Optional[JSONDict], bot: "Bot", credentials: "FileCredentials"
|
||||
|
||||
@@ -56,7 +56,7 @@ class OrderInfo(TelegramObject):
|
||||
name: Optional[str] = None,
|
||||
phone_number: Optional[str] = None,
|
||||
email: Optional[str] = None,
|
||||
shipping_address: Optional[str] = None,
|
||||
shipping_address: Optional[ShippingAddress] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
@@ -64,7 +64,7 @@ class OrderInfo(TelegramObject):
|
||||
self.name: Optional[str] = name
|
||||
self.phone_number: Optional[str] = phone_number
|
||||
self.email: Optional[str] = email
|
||||
self.shipping_address: Optional[str] = shipping_address
|
||||
self.shipping_address: Optional[ShippingAddress] = shipping_address
|
||||
|
||||
self._id_attrs = (self.name, self.phone_number, self.email, self.shipping_address)
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ class PreCheckoutQuery(TelegramObject):
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.id: str = id # pylint: disable=invalid-name
|
||||
self.id: str = id
|
||||
self.from_user: User = from_user
|
||||
self.currency: str = currency
|
||||
self.total_amount: int = total_amount
|
||||
@@ -120,7 +120,7 @@ class PreCheckoutQuery(TelegramObject):
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
async def answer( # pylint: disable=invalid-name
|
||||
async def answer(
|
||||
self,
|
||||
ok: bool,
|
||||
error_message: Optional[str] = None,
|
||||
|
||||
@@ -66,7 +66,7 @@ class ShippingOption(TelegramObject):
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
|
||||
self.id: str = id # pylint: disable=invalid-name
|
||||
self.id: str = id
|
||||
self.title: str = title
|
||||
self.prices: Tuple[LabeledPrice, ...] = parse_sequence_arg(prices)
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
from typing import TYPE_CHECKING, Optional, Sequence
|
||||
|
||||
from telegram._payment.shippingaddress import ShippingAddress
|
||||
from telegram._payment.shippingoption import ShippingOption
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
@@ -29,6 +28,7 @@ from telegram._utils.types import JSONDict, ODVInput
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
from telegram._payment.shippingoption import ShippingOption
|
||||
|
||||
|
||||
class ShippingQuery(TelegramObject):
|
||||
@@ -67,7 +67,7 @@ class ShippingQuery(TelegramObject):
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.id: str = id # pylint: disable=invalid-name
|
||||
self.id: str = id
|
||||
self.from_user: User = from_user
|
||||
self.invoice_payload: str = invoice_payload
|
||||
self.shipping_address: ShippingAddress = shipping_address
|
||||
@@ -89,10 +89,10 @@ class ShippingQuery(TelegramObject):
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
async def answer( # pylint: disable=invalid-name
|
||||
async def answer(
|
||||
self,
|
||||
ok: bool,
|
||||
shipping_options: Optional[Sequence[ShippingOption]] = None,
|
||||
shipping_options: Optional[Sequence["ShippingOption"]] = None,
|
||||
error_message: Optional[str] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
||||
+8
-21
@@ -29,8 +29,6 @@ from telegram._utils import enum
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
@@ -89,9 +87,11 @@ class PollAnswer(TelegramObject):
|
||||
|
||||
.. versionchanged:: 20.5
|
||||
The order of :paramref:`option_ids` and :paramref:`user` is changed in
|
||||
20.5 as the latter one became optional. We currently provide
|
||||
backward compatibility for this but it will be removed in the future.
|
||||
Please update your code to use the new order.
|
||||
20.5 as the latter one became optional.
|
||||
|
||||
.. versionchanged:: 20.6
|
||||
Backward compatiblity for changed order of :paramref:`option_ids` and :paramref:`user`
|
||||
was removed.
|
||||
|
||||
Args:
|
||||
poll_id (:obj:`str`): Unique poll identifier.
|
||||
@@ -145,21 +145,8 @@ class PollAnswer(TelegramObject):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.poll_id: str = poll_id
|
||||
self.voter_chat: Optional[Chat] = voter_chat
|
||||
|
||||
if isinstance(option_ids, User) or isinstance(user, tuple):
|
||||
warn(
|
||||
"From v20.5 the order of `option_ids` and `user` is changed as the latter one"
|
||||
" became optional. Please update your code to use the new order.",
|
||||
category=PTBDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
self.option_ids: Tuple[int, ...] = parse_sequence_arg(user)
|
||||
self.user: Optional[User] = option_ids
|
||||
else:
|
||||
self.option_ids: Tuple[int, ...] = parse_sequence_arg( # type: ignore[no-redef]
|
||||
option_ids
|
||||
)
|
||||
self.user: Optional[User] = user # type: ignore[no-redef]
|
||||
self.option_ids: Tuple[int, ...] = parse_sequence_arg(option_ids)
|
||||
self.user: Optional[User] = user
|
||||
|
||||
self._id_attrs = (
|
||||
self.poll_id,
|
||||
@@ -302,7 +289,7 @@ class Poll(TelegramObject):
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.id: str = id # pylint: disable=invalid-name
|
||||
self.id: str = id
|
||||
self.question: str = question
|
||||
self.options: Tuple[PollOption, ...] = parse_sequence_arg(options)
|
||||
self.total_voter_count: int = total_voter_count
|
||||
|
||||
+156
-156
@@ -110,43 +110,53 @@ class TelegramObject:
|
||||
# We don't do anything with api_kwargs here - see docstring of _apply_api_kwargs
|
||||
self.api_kwargs: Mapping[str, Any] = MappingProxyType(api_kwargs or {})
|
||||
|
||||
def _freeze(self) -> None:
|
||||
self._frozen = True
|
||||
def __eq__(self, other: object) -> bool:
|
||||
"""Compares this object with :paramref:`other` in terms of equality.
|
||||
If this object and :paramref:`other` are `not` objects of the same class,
|
||||
this comparison will fall back to Python's default implementation of :meth:`object.__eq__`.
|
||||
Otherwise, both objects may be compared in terms of equality, if the corresponding
|
||||
subclass of :class:`TelegramObject` has defined a set of attributes to compare and
|
||||
the objects are considered to be equal, if all of these attributes are equal.
|
||||
If the subclass has not defined a set of attributes to compare, a warning will be issued.
|
||||
|
||||
def _unfreeze(self) -> None:
|
||||
self._frozen = False
|
||||
Tip:
|
||||
If instances of a class in the :mod:`telegram` module are comparable in terms of
|
||||
equality, the documentation of the class will state the attributes that will be used
|
||||
for this comparison.
|
||||
|
||||
@contextmanager
|
||||
def _unfrozen(self: Tele_co) -> Iterator[Tele_co]:
|
||||
"""Context manager to temporarily unfreeze the object. For internal use only.
|
||||
Args:
|
||||
other (:obj:`object`): The object to compare with.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`
|
||||
|
||||
Note:
|
||||
with to._unfrozen() as other_to:
|
||||
assert to is other_to
|
||||
"""
|
||||
self._unfreeze()
|
||||
yield self
|
||||
self._freeze()
|
||||
if isinstance(other, self.__class__):
|
||||
if not self._id_attrs:
|
||||
warn(
|
||||
f"Objects of type {self.__class__.__name__} can not be meaningfully tested for"
|
||||
" equivalence.",
|
||||
stacklevel=2,
|
||||
)
|
||||
if not other._id_attrs:
|
||||
warn(
|
||||
f"Objects of type {other.__class__.__name__} can not be meaningfully tested"
|
||||
" for equivalence.",
|
||||
stacklevel=2,
|
||||
)
|
||||
return self._id_attrs == other._id_attrs
|
||||
return super().__eq__(other)
|
||||
|
||||
def _apply_api_kwargs(self, api_kwargs: JSONDict) -> None:
|
||||
"""Loops through the api kwargs and for every key that exists as attribute of the
|
||||
object (and is None), it moves the value from `api_kwargs` to the attribute.
|
||||
*Edits `api_kwargs` in place!*
|
||||
def __hash__(self) -> int:
|
||||
"""Builds a hash value for this object such that the hash of two objects is equal if and
|
||||
only if the objects are equal in terms of :meth:`__eq__`.
|
||||
|
||||
This method is currently only called in the unpickling process, i.e. not on "normal" init.
|
||||
This is because
|
||||
* automating this is tricky to get right: It should be called at the *end* of the __init__,
|
||||
preferably only once at the end of the __init__ of the last child class. This could be
|
||||
done via __init_subclass__, but it's hard to not destroy the signature of __init__ in the
|
||||
process.
|
||||
* calling it manually in every __init__ is tedious
|
||||
* There probably is no use case for it anyway. If you manually initialize a TO subclass,
|
||||
then you can pass everything as proper argument.
|
||||
Returns:
|
||||
:obj:`int`
|
||||
"""
|
||||
# we convert to list to ensure that the list doesn't change length while we loop
|
||||
for key in list(api_kwargs.keys()):
|
||||
if getattr(self, key, True) is None:
|
||||
setattr(self, key, api_kwargs.pop(key))
|
||||
if self._id_attrs:
|
||||
return hash((self.__class__, self._id_attrs))
|
||||
return super().__hash__()
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
"""Overrides :meth:`object.__setattr__` to prevent the overriding of attributes.
|
||||
@@ -364,6 +374,122 @@ class TelegramObject:
|
||||
self.set_bot(bot)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _parse_data(data: Optional[JSONDict]) -> Optional[JSONDict]:
|
||||
"""Should be called by subclasses that override de_json to ensure that the input
|
||||
is not altered. Whoever calls de_json might still want to use the original input
|
||||
for something else.
|
||||
"""
|
||||
return None if data is None else data.copy()
|
||||
|
||||
@classmethod
|
||||
def _de_json(
|
||||
cls: Type[Tele_co],
|
||||
data: Optional[JSONDict],
|
||||
bot: "Bot",
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> Optional[Tele_co]:
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
# try-except is significantly faster in case we already have a correct argument set
|
||||
try:
|
||||
obj = cls(**data, api_kwargs=api_kwargs)
|
||||
except TypeError as exc:
|
||||
if "__init__() got an unexpected keyword argument" not in str(exc):
|
||||
raise exc
|
||||
|
||||
if cls.__INIT_PARAMS_CHECK is not cls:
|
||||
signature = inspect.signature(cls)
|
||||
cls.__INIT_PARAMS = set(signature.parameters.keys())
|
||||
cls.__INIT_PARAMS_CHECK = cls
|
||||
|
||||
api_kwargs = api_kwargs or {}
|
||||
existing_kwargs: JSONDict = {}
|
||||
for key, value in data.items():
|
||||
(existing_kwargs if key in cls.__INIT_PARAMS else api_kwargs)[key] = value
|
||||
|
||||
obj = cls(api_kwargs=api_kwargs, **existing_kwargs)
|
||||
|
||||
obj.set_bot(bot=bot)
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def de_json(cls: Type[Tele_co], data: Optional[JSONDict], bot: "Bot") -> Optional[Tele_co]:
|
||||
"""Converts JSON data to a Telegram object.
|
||||
|
||||
Args:
|
||||
data (Dict[:obj:`str`, ...]): The JSON data.
|
||||
bot (:class:`telegram.Bot`): The bot associated with this object.
|
||||
|
||||
Returns:
|
||||
The Telegram object.
|
||||
|
||||
"""
|
||||
return cls._de_json(data=data, bot=bot)
|
||||
|
||||
@classmethod
|
||||
def de_list(
|
||||
cls: Type[Tele_co], data: Optional[List[JSONDict]], bot: "Bot"
|
||||
) -> Tuple[Tele_co, ...]:
|
||||
"""Converts a list of JSON objects to a tuple of Telegram objects.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|
||||
* Returns a tuple instead of a list.
|
||||
* Filters out any :obj:`None` values.
|
||||
|
||||
Args:
|
||||
data (List[Dict[:obj:`str`, ...]]): The JSON data.
|
||||
bot (:class:`telegram.Bot`): The bot associated with these objects.
|
||||
|
||||
Returns:
|
||||
A tuple of Telegram objects.
|
||||
|
||||
"""
|
||||
if not data:
|
||||
return ()
|
||||
|
||||
return tuple(obj for obj in (cls.de_json(d, bot) for d in data) if obj is not None)
|
||||
|
||||
@contextmanager
|
||||
def _unfrozen(self: Tele_co) -> Iterator[Tele_co]:
|
||||
"""Context manager to temporarily unfreeze the object. For internal use only.
|
||||
|
||||
Note:
|
||||
with to._unfrozen() as other_to:
|
||||
assert to is other_to
|
||||
"""
|
||||
self._unfreeze()
|
||||
yield self
|
||||
self._freeze()
|
||||
|
||||
def _freeze(self) -> None:
|
||||
self._frozen = True
|
||||
|
||||
def _unfreeze(self) -> None:
|
||||
self._frozen = False
|
||||
|
||||
def _apply_api_kwargs(self, api_kwargs: JSONDict) -> None:
|
||||
"""Loops through the api kwargs and for every key that exists as attribute of the
|
||||
object (and is None), it moves the value from `api_kwargs` to the attribute.
|
||||
*Edits `api_kwargs` in place!*
|
||||
|
||||
This method is currently only called in the unpickling process, i.e. not on "normal" init.
|
||||
This is because
|
||||
* automating this is tricky to get right: It should be called at the *end* of the __init__,
|
||||
preferably only once at the end of the __init__ of the last child class. This could be
|
||||
done via __init_subclass__, but it's hard to not destroy the signature of __init__ in the
|
||||
process.
|
||||
* calling it manually in every __init__ is tedious
|
||||
* There probably is no use case for it anyway. If you manually initialize a TO subclass,
|
||||
then you can pass everything as proper argument.
|
||||
"""
|
||||
# we convert to list to ensure that the list doesn't change length while we loop
|
||||
for key in list(api_kwargs.keys()):
|
||||
if getattr(self, key, True) is None:
|
||||
setattr(self, key, api_kwargs.pop(key))
|
||||
|
||||
def _get_attrs_names(self, include_private: bool) -> Iterator[str]:
|
||||
"""
|
||||
Returns the names of the attributes of this object. This is used to determine which
|
||||
@@ -423,84 +549,6 @@ class TelegramObject:
|
||||
data.pop("_bot", None)
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _parse_data(data: Optional[JSONDict]) -> Optional[JSONDict]:
|
||||
"""Should be called by subclasses that override de_json to ensure that the input
|
||||
is not altered. Whoever calls de_json might still want to use the original input
|
||||
for something else.
|
||||
"""
|
||||
return None if data is None else data.copy()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls: Type[Tele_co], data: Optional[JSONDict], bot: "Bot") -> Optional[Tele_co]:
|
||||
"""Converts JSON data to a Telegram object.
|
||||
|
||||
Args:
|
||||
data (Dict[:obj:`str`, ...]): The JSON data.
|
||||
bot (:class:`telegram.Bot`): The bot associated with this object.
|
||||
|
||||
Returns:
|
||||
The Telegram object.
|
||||
|
||||
"""
|
||||
return cls._de_json(data=data, bot=bot)
|
||||
|
||||
@classmethod
|
||||
def _de_json(
|
||||
cls: Type[Tele_co],
|
||||
data: Optional[JSONDict],
|
||||
bot: "Bot",
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> Optional[Tele_co]:
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
# try-except is significantly faster in case we already have a correct argument set
|
||||
try:
|
||||
obj = cls(**data, api_kwargs=api_kwargs)
|
||||
except TypeError as exc:
|
||||
if "__init__() got an unexpected keyword argument" not in str(exc):
|
||||
raise exc
|
||||
|
||||
if cls.__INIT_PARAMS_CHECK is not cls:
|
||||
signature = inspect.signature(cls)
|
||||
cls.__INIT_PARAMS = set(signature.parameters.keys())
|
||||
cls.__INIT_PARAMS_CHECK = cls
|
||||
|
||||
api_kwargs = api_kwargs or {}
|
||||
existing_kwargs: JSONDict = {}
|
||||
for key, value in data.items():
|
||||
(existing_kwargs if key in cls.__INIT_PARAMS else api_kwargs)[key] = value
|
||||
|
||||
obj = cls(api_kwargs=api_kwargs, **existing_kwargs)
|
||||
|
||||
obj.set_bot(bot=bot)
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def de_list(
|
||||
cls: Type[Tele_co], data: Optional[List[JSONDict]], bot: "Bot"
|
||||
) -> Tuple[Tele_co, ...]:
|
||||
"""Converts a list of JSON objects to a tuple of Telegram objects.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|
||||
* Returns a tuple instead of a list.
|
||||
* Filters out any :obj:`None` values.
|
||||
|
||||
Args:
|
||||
data (List[Dict[:obj:`str`, ...]]): The JSON data.
|
||||
bot (:class:`telegram.Bot`): The bot associated with these objects.
|
||||
|
||||
Returns:
|
||||
A tuple of Telegram objects.
|
||||
|
||||
"""
|
||||
if not data:
|
||||
return ()
|
||||
|
||||
return tuple(obj for obj in (cls.de_json(d, bot) for d in data) if obj is not None)
|
||||
|
||||
def to_json(self) -> str:
|
||||
"""Gives a JSON representation of object.
|
||||
|
||||
@@ -596,51 +644,3 @@ class TelegramObject:
|
||||
bot (:class:`telegram.Bot` | :obj:`None`): The bot instance.
|
||||
"""
|
||||
self._bot = bot
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
"""Compares this object with :paramref:`other` in terms of equality.
|
||||
If this object and :paramref:`other` are `not` objects of the same class,
|
||||
this comparison will fall back to Python's default implementation of :meth:`object.__eq__`.
|
||||
Otherwise, both objects may be compared in terms of equality, if the corresponding
|
||||
subclass of :class:`TelegramObject` has defined a set of attributes to compare and
|
||||
the objects are considered to be equal, if all of these attributes are equal.
|
||||
If the subclass has not defined a set of attributes to compare, a warning will be issued.
|
||||
|
||||
Tip:
|
||||
If instances of a class in the :mod:`telegram` module are comparable in terms of
|
||||
equality, the documentation of the class will state the attributes that will be used
|
||||
for this comparison.
|
||||
|
||||
Args:
|
||||
other (:obj:`object`): The object to compare with.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`
|
||||
|
||||
"""
|
||||
if isinstance(other, self.__class__):
|
||||
if not self._id_attrs:
|
||||
warn(
|
||||
f"Objects of type {self.__class__.__name__} can not be meaningfully tested for"
|
||||
" equivalence.",
|
||||
stacklevel=2,
|
||||
)
|
||||
if not other._id_attrs:
|
||||
warn(
|
||||
f"Objects of type {other.__class__.__name__} can not be meaningfully tested"
|
||||
" for equivalence.",
|
||||
stacklevel=2,
|
||||
)
|
||||
return self._id_attrs == other._id_attrs
|
||||
return super().__eq__(other)
|
||||
|
||||
def __hash__(self) -> int:
|
||||
"""Builds a hash value for this object such that the hash of two objects is equal if and
|
||||
only if the objects are equal in terms of :meth:`__eq__`.
|
||||
|
||||
Returns:
|
||||
:obj:`int`
|
||||
"""
|
||||
if self._id_attrs:
|
||||
return hash((self.__class__, self._id_attrs))
|
||||
return super().__hash__()
|
||||
|
||||
+1
-1
@@ -153,7 +153,7 @@ class User(TelegramObject):
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
# Required
|
||||
self.id: int = id # pylint: disable=invalid-name
|
||||
self.id: int = id
|
||||
self.first_name: str = first_name
|
||||
self.is_bot: bool = is_bot
|
||||
# Optionals
|
||||
|
||||
@@ -53,7 +53,7 @@ def _localize(datetime: dtm.datetime, tzinfo: dtm.tzinfo) -> dtm.datetime:
|
||||
|
||||
|
||||
def to_float_timestamp(
|
||||
time_object: Union[int, float, dtm.timedelta, dtm.datetime, dtm.time],
|
||||
time_object: Union[float, dtm.timedelta, dtm.datetime, dtm.time],
|
||||
reference_timestamp: Optional[float] = None,
|
||||
tzinfo: Optional[dtm.tzinfo] = None,
|
||||
) -> float:
|
||||
@@ -65,12 +65,11 @@ def to_float_timestamp(
|
||||
to be in UTC, if ``bot`` is not passed or ``bot.defaults`` is :obj:`None`.
|
||||
|
||||
Args:
|
||||
time_object (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta` | \
|
||||
time_object (:obj:`float` | :obj:`datetime.timedelta` | \
|
||||
:obj:`datetime.datetime` | :obj:`datetime.time`):
|
||||
Time value to convert. The semantics of this parameter will depend on its type:
|
||||
|
||||
* :obj:`int` or :obj:`float` will be interpreted as "seconds from
|
||||
:paramref:`reference_t`"
|
||||
* :obj:`float` will be interpreted as "seconds from :paramref:`reference_t`"
|
||||
* :obj:`datetime.timedelta` will be interpreted as
|
||||
"time increment from :paramref:`reference_timestamp`"
|
||||
* :obj:`datetime.datetime` will be interpreted as an absolute date/time value
|
||||
@@ -148,7 +147,7 @@ def to_float_timestamp(
|
||||
|
||||
|
||||
def to_timestamp(
|
||||
dt_obj: Union[int, float, dtm.timedelta, dtm.datetime, dtm.time, None],
|
||||
dt_obj: Union[float, dtm.timedelta, dtm.datetime, dtm.time, None],
|
||||
reference_timestamp: Optional[float] = None,
|
||||
tzinfo: Optional[dtm.tzinfo] = None,
|
||||
) -> Optional[int]:
|
||||
|
||||
@@ -88,6 +88,14 @@ class DefaultValue(Generic[DVType]):
|
||||
def __bool__(self) -> bool:
|
||||
return bool(self.value)
|
||||
|
||||
# This is mostly here for readability during debugging
|
||||
def __str__(self) -> str:
|
||||
return f"DefaultValue({self.value})"
|
||||
|
||||
# This is here to have the default instances nicely rendered in the docs
|
||||
def __repr__(self) -> str:
|
||||
return repr(self.value)
|
||||
|
||||
@overload
|
||||
@staticmethod
|
||||
def get_value(obj: "DefaultValue[OT]") -> OT:
|
||||
@@ -112,14 +120,6 @@ class DefaultValue(Generic[DVType]):
|
||||
"""
|
||||
return obj.value if isinstance(obj, DefaultValue) else obj
|
||||
|
||||
# This is mostly here for readability during debugging
|
||||
def __str__(self) -> str:
|
||||
return f"DefaultValue({self.value})"
|
||||
|
||||
# This is here to have the default instances nicely rendered in the docs
|
||||
def __repr__(self) -> str:
|
||||
return repr(self.value)
|
||||
|
||||
|
||||
DEFAULT_NONE: DefaultValue[None] = DefaultValue(None)
|
||||
""":class:`DefaultValue`: Default :obj:`None`"""
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2023
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains auxiliary functionality for building strings for __repr__ method.
|
||||
|
||||
Warning:
|
||||
Contents of this module are intended to be used internally by the library and *not* by the
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
from typing import Any
|
||||
|
||||
|
||||
def build_repr_with_selected_attrs(obj: object, **kwargs: Any) -> str:
|
||||
"""Create ``__repr__`` string in the style ``Classname[arg1=1, arg2=2]``.
|
||||
|
||||
The square brackets emphasize the fact that an object cannot be instantiated
|
||||
from this string.
|
||||
|
||||
Attributes that are to be used in the representation, are passed as kwargs.
|
||||
"""
|
||||
return (
|
||||
f"{obj.__class__.__name__}"
|
||||
# square brackets emphasize that an object cannot be instantiated with these params
|
||||
f"[{', '.join(_stringify(name, value) for name, value in kwargs.items())}]"
|
||||
)
|
||||
|
||||
|
||||
def _stringify(key: str, val: Any) -> str:
|
||||
return f"{key}={val.__qualname__ if callable(val) else val}"
|
||||
@@ -51,7 +51,7 @@ class Version(NamedTuple):
|
||||
|
||||
|
||||
__version_info__: Final[Version] = Version(
|
||||
major=20, minor=5, micro=0, releaselevel="final", serial=0
|
||||
major=20, minor=6, micro=0, releaselevel="final", serial=0
|
||||
)
|
||||
__version__: Final[str] = str(__version_info__)
|
||||
|
||||
|
||||
@@ -39,12 +39,12 @@ class WebAppInfo(TelegramObject):
|
||||
Args:
|
||||
url (:obj:`str`): An HTTPS URL of a Web App to be opened with additional data as specified
|
||||
in `Initializing Web Apps \
|
||||
<https://core.telegram.org/bots/webapps#initializing-web-apps>`_.
|
||||
<https://core.telegram.org/bots/webapps#initializing-mini-apps>`_.
|
||||
|
||||
Attributes:
|
||||
url (:obj:`str`): An HTTPS URL of a Web App to be opened with additional data as specified
|
||||
in `Initializing Web Apps \
|
||||
<https://core.telegram.org/bots/webapps#initializing-web-apps>`_.
|
||||
<https://core.telegram.org/bots/webapps#initializing-mini-apps>`_.
|
||||
"""
|
||||
|
||||
__slots__ = ("url",)
|
||||
|
||||
@@ -26,28 +26,63 @@ from telegram._utils.types import JSONDict
|
||||
class WriteAccessAllowed(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a user allowing a bot to write messages after
|
||||
adding the bot to the attachment menu or launching a Web App from a link.
|
||||
adding it to the attachment menu, launching a Web App from a link, or accepting an explicit
|
||||
request from a Web App sent by the method
|
||||
`requestWriteAccess <https://core.telegram.org/bots/webapps#initializing-mini-apps>`_.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`web_app_name` is equal.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
.. versionchanged:: 20.6
|
||||
Added custom equality comparison for objects of this class.
|
||||
|
||||
Args:
|
||||
web_app_name (:obj:`str`, optional): Name of the Web App which was launched from a link.
|
||||
web_app_name (:obj:`str`, optional): Name of the Web App, if the access was granted when
|
||||
the Web App was launched from a link.
|
||||
|
||||
.. versionadded:: 20.3
|
||||
from_request (:obj:`bool`, optional): :obj:`True`, if the access was granted after the user
|
||||
accepted an explicit request from a Web App sent by the method
|
||||
`requestWriteAccess <https://core.telegram.org/bots/webapps#initializing-mini-apps>`_.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
from_attachment_menu (:obj:`bool`, optional): :obj:`True`, if the access was granted when
|
||||
the bot was added to the attachment or side menu.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
|
||||
Attributes:
|
||||
web_app_name (:obj:`str`): Optional. Name of the Web App which was launched from a link.
|
||||
web_app_name (:obj:`str`): Optional. Name of the Web App, if the access was granted when
|
||||
the Web App was launched from a link.
|
||||
|
||||
.. versionadded:: 20.3
|
||||
from_request (:obj:`bool`): Optional. :obj:`True`, if the access was granted after the user
|
||||
accepted an explicit request from a Web App.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
from_attachment_menu (:obj:`bool`): Optional. :obj:`True`, if the access was granted when
|
||||
the bot was added to the attachment or side menu.
|
||||
|
||||
.. versionadded:: 20.6
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("web_app_name",)
|
||||
__slots__ = ("web_app_name", "from_request", "from_attachment_menu")
|
||||
|
||||
def __init__(
|
||||
self, web_app_name: Optional[str] = None, *, api_kwargs: Optional[JSONDict] = None
|
||||
self,
|
||||
web_app_name: Optional[str] = None,
|
||||
from_request: Optional[bool] = None,
|
||||
from_attachment_menu: Optional[bool] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.web_app_name: Optional[str] = web_app_name
|
||||
self.from_request: Optional[bool] = from_request
|
||||
self.from_attachment_menu: Optional[bool] = from_attachment_menu
|
||||
|
||||
self._id_attrs = (self.web_app_name,)
|
||||
|
||||
self._freeze()
|
||||
|
||||
@@ -116,7 +116,7 @@ class _BotAPIVersion(NamedTuple):
|
||||
#: :data:`telegram.__bot_api_version_info__`.
|
||||
#:
|
||||
#: .. versionadded:: 20.0
|
||||
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=6, minor=8)
|
||||
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=6, minor=9)
|
||||
#: :obj:`str`: Telegram Bot API
|
||||
#: version supported by this version of `python-telegram-bot`. Also available as
|
||||
#: :data:`telegram.__bot_api_version__`.
|
||||
|
||||
@@ -54,6 +54,7 @@ from typing import (
|
||||
from telegram._update import Update
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE, DEFAULT_TRUE, DefaultValue
|
||||
from telegram._utils.logging import get_logger
|
||||
from telegram._utils.repr import build_repr_with_selected_attrs
|
||||
from telegram._utils.types import SCT, DVType, ODVInput
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.error import TelegramError
|
||||
@@ -80,7 +81,6 @@ _AppType = TypeVar("_AppType", bound="Application") # pylint: disable=invalid-n
|
||||
_STOP_SIGNAL = object()
|
||||
_DEFAULT_0 = DefaultValue(0)
|
||||
|
||||
|
||||
# Since python 3.12, the coroutine passed to create_task should not be an (async) generator. Remove
|
||||
# this check when we drop support for python 3.11.
|
||||
if sys.version_info >= (3, 12):
|
||||
@@ -90,7 +90,6 @@ else:
|
||||
|
||||
_ErrorCoroType = Optional[_CoroType[RT]]
|
||||
|
||||
|
||||
_LOGGER = get_logger(__name__)
|
||||
|
||||
|
||||
@@ -345,11 +344,36 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
self.__update_persistence_lock = asyncio.Lock()
|
||||
self.__create_task_tasks: Set[asyncio.Task] = set() # Used for awaiting tasks upon exit
|
||||
|
||||
def _check_initialized(self) -> None:
|
||||
if not self._initialized:
|
||||
raise RuntimeError(
|
||||
"This Application was not initialized via `Application.initialize`!"
|
||||
)
|
||||
async def __aenter__(self: _AppType) -> _AppType: # noqa: PYI019
|
||||
"""Simple context manager which initializes the App."""
|
||||
try:
|
||||
await self.initialize()
|
||||
return self
|
||||
except Exception as exc:
|
||||
await self.shutdown()
|
||||
raise exc
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
"""Shutdown the App from the context manager."""
|
||||
# Make sure not to return `True` so that exceptions are not suppressed
|
||||
# https://docs.python.org/3/reference/datamodel.html?#object.__aexit__
|
||||
await self.shutdown()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Give a string representation of the application in the form ``Application[bot=...]``.
|
||||
|
||||
As this class doesn't implement :meth:`object.__str__`, the default implementation
|
||||
will be used, which is equivalent to :meth:`__repr__`.
|
||||
|
||||
Returns:
|
||||
:obj:`str`
|
||||
"""
|
||||
return build_repr_with_selected_attrs(self, bot=self.bot)
|
||||
|
||||
@property
|
||||
def running(self) -> bool:
|
||||
@@ -400,6 +424,27 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
"""
|
||||
return self._update_processor
|
||||
|
||||
@staticmethod
|
||||
def _raise_system_exit() -> NoReturn:
|
||||
raise SystemExit
|
||||
|
||||
@staticmethod
|
||||
def builder() -> "InitApplicationBuilder":
|
||||
"""Convenience method. Returns a new :class:`telegram.ext.ApplicationBuilder`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
"""
|
||||
# Unfortunately this needs to be here due to cyclical imports
|
||||
from telegram.ext import ApplicationBuilder # pylint: disable=import-outside-toplevel
|
||||
|
||||
return ApplicationBuilder()
|
||||
|
||||
def _check_initialized(self) -> None:
|
||||
if not self._initialized:
|
||||
raise RuntimeError(
|
||||
"This Application was not initialized via `Application.initialize`!"
|
||||
)
|
||||
|
||||
async def initialize(self) -> None:
|
||||
"""Initializes the Application by initializing:
|
||||
|
||||
@@ -486,26 +531,6 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
|
||||
self._initialized = False
|
||||
|
||||
async def __aenter__(self: _AppType) -> _AppType:
|
||||
"""Simple context manager which initializes the App."""
|
||||
try:
|
||||
await self.initialize()
|
||||
return self
|
||||
except Exception as exc:
|
||||
await self.shutdown()
|
||||
raise exc
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
"""Shutdown the App from the context manager."""
|
||||
# Make sure not to return `True` so that exceptions are not suppressed
|
||||
# https://docs.python.org/3/reference/datamodel.html?#object.__aexit__
|
||||
await self.shutdown()
|
||||
|
||||
async def _initialize_persistence(self) -> None:
|
||||
"""This method basically just loads all the data by awaiting the BP methods"""
|
||||
if not self.persistence:
|
||||
@@ -535,17 +560,6 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
persistent_data
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def builder() -> "InitApplicationBuilder":
|
||||
"""Convenience method. Returns a new :class:`telegram.ext.ApplicationBuilder`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
"""
|
||||
# Unfortunately this needs to be here due to cyclical imports
|
||||
from telegram.ext import ApplicationBuilder # pylint: disable=import-outside-toplevel
|
||||
|
||||
return ApplicationBuilder()
|
||||
|
||||
async def start(self) -> None:
|
||||
"""Starts
|
||||
|
||||
@@ -912,10 +926,6 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
stop_signals=stop_signals,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _raise_system_exit() -> NoReturn:
|
||||
raise SystemExit
|
||||
|
||||
def __run(
|
||||
self,
|
||||
updater_coroutine: Coroutine,
|
||||
@@ -938,7 +948,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
warn(
|
||||
f"Could not add signal handlers for the stop signals {stop_signals} due to "
|
||||
f"exception `{exc!r}`. If your event loop does not implement `add_signal_handler`,"
|
||||
f" please pass `stop_signals=None`.",
|
||||
" please pass `stop_signals=None`.",
|
||||
stacklevel=3,
|
||||
)
|
||||
|
||||
@@ -1070,8 +1080,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
except Exception as exception:
|
||||
if isinstance(exception, ApplicationHandlerStop):
|
||||
warn(
|
||||
"ApplicationHandlerStop is not supported with handlers "
|
||||
"running non-blocking.",
|
||||
"ApplicationHandlerStop is not supported with handlers running non-blocking.",
|
||||
stacklevel=1,
|
||||
)
|
||||
|
||||
@@ -1176,8 +1185,10 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
self.create_task(
|
||||
coroutine,
|
||||
update=update,
|
||||
name=f"Application:{self.bot.id}:process_update_non_blocking"
|
||||
f":{handler}",
|
||||
name=(
|
||||
f"Application:{self.bot.id}:process_update_non_blocking"
|
||||
f":{handler}"
|
||||
),
|
||||
)
|
||||
else:
|
||||
any_blocking = True
|
||||
@@ -1246,7 +1257,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
|
||||
if not self.persistence:
|
||||
raise ValueError(
|
||||
f"ConversationHandler {handler.name} "
|
||||
f"can not be persistent if application has no persistence"
|
||||
"can not be persistent if application has no persistence"
|
||||
)
|
||||
if self._initialized:
|
||||
self.create_task(
|
||||
|
||||
@@ -21,6 +21,7 @@ from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Any, Generic, Optional, TypeVar, Union
|
||||
|
||||
from telegram._utils.defaultvalue import DEFAULT_TRUE
|
||||
from telegram._utils.repr import build_repr_with_selected_attrs
|
||||
from telegram._utils.types import DVType
|
||||
from telegram.ext._utils.types import CCT, HandlerCallback
|
||||
|
||||
@@ -95,6 +96,17 @@ class BaseHandler(Generic[UT, CCT], ABC):
|
||||
self.callback: HandlerCallback[UT, CCT, RT] = callback
|
||||
self.block: DVType[bool] = block
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Give a string representation of the handler in the form ``ClassName[callback=...]``.
|
||||
|
||||
As this class doesn't implement :meth:`object.__str__`, the default implementation
|
||||
will be used, which is equivalent to :meth:`__repr__`.
|
||||
|
||||
Returns:
|
||||
:obj:`str`
|
||||
"""
|
||||
return build_repr_with_selected_attrs(self, callback=self.callback.__qualname__)
|
||||
|
||||
@abstractmethod
|
||||
def check_update(self, update: object) -> Optional[Union[bool, object]]:
|
||||
"""
|
||||
|
||||
@@ -48,6 +48,24 @@ class BaseUpdateProcessor(ABC):
|
||||
raise ValueError("`max_concurrent_updates` must be a positive integer!")
|
||||
self._semaphore = BoundedSemaphore(self.max_concurrent_updates)
|
||||
|
||||
async def __aenter__(self) -> "BaseUpdateProcessor":
|
||||
"""Simple context manager which initializes the Processor."""
|
||||
try:
|
||||
await self.initialize()
|
||||
return self
|
||||
except Exception as exc:
|
||||
await self.shutdown()
|
||||
raise exc
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
"""Simple context manager which shuts down the Processor."""
|
||||
await self.shutdown()
|
||||
|
||||
@property
|
||||
def max_concurrent_updates(self) -> int:
|
||||
""":obj:`int`: The maximum number of updates that can be processed concurrently."""
|
||||
@@ -105,24 +123,6 @@ class BaseUpdateProcessor(ABC):
|
||||
async with self._semaphore:
|
||||
await self.do_process_update(update, coroutine)
|
||||
|
||||
async def __aenter__(self) -> "BaseUpdateProcessor":
|
||||
"""Simple context manager which initializes the Processor."""
|
||||
try:
|
||||
await self.initialize()
|
||||
return self
|
||||
except Exception as exc:
|
||||
await self.shutdown()
|
||||
raise exc
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
"""Shutdown the Processor from the context manager."""
|
||||
await self.shutdown()
|
||||
|
||||
|
||||
class SimpleUpdateProcessor(BaseUpdateProcessor):
|
||||
"""Instance of :class:`telegram.ext.BaseUpdateProcessor` that immediately awaits the
|
||||
|
||||
@@ -227,14 +227,16 @@ class CallbackDataCache:
|
||||
# Built a new nested list of buttons by replacing the callback data if needed
|
||||
buttons = [
|
||||
[
|
||||
# We create a new button instead of replacing callback_data in case the
|
||||
# same object is used elsewhere
|
||||
InlineKeyboardButton(
|
||||
btn.text,
|
||||
callback_data=self.__put_button(btn.callback_data, keyboard_data),
|
||||
(
|
||||
# We create a new button instead of replacing callback_data in case the
|
||||
# same object is used elsewhere
|
||||
InlineKeyboardButton(
|
||||
btn.text,
|
||||
callback_data=self.__put_button(btn.callback_data, keyboard_data),
|
||||
)
|
||||
if btn.callback_data
|
||||
else btn
|
||||
)
|
||||
if btn.callback_data
|
||||
else btn
|
||||
for btn in column
|
||||
]
|
||||
for column in reply_markup.inline_keyboard
|
||||
|
||||
@@ -38,6 +38,7 @@ from typing import (
|
||||
from telegram import Update
|
||||
from telegram._utils.defaultvalue import DEFAULT_TRUE, DefaultValue
|
||||
from telegram._utils.logging import get_logger
|
||||
from telegram._utils.repr import build_repr_with_selected_attrs
|
||||
from telegram._utils.types import DVType
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.ext._application import ApplicationHandlerStop
|
||||
@@ -413,7 +414,7 @@ class ConversationHandler(BaseHandler[Update, CCT]):
|
||||
):
|
||||
warn(
|
||||
f"Updates handled by {handler.__class__.__name__} only have information about "
|
||||
f"the user, so this handler won't ever be triggered if `per_chat=True`."
|
||||
"the user, so this handler won't ever be triggered if `per_chat=True`."
|
||||
f"{per_faq_link}",
|
||||
stacklevel=2,
|
||||
)
|
||||
@@ -440,6 +441,30 @@ class ConversationHandler(BaseHandler[Update, CCT]):
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Give a string representation of the ConversationHandler in the form
|
||||
``ConversationHandler[name=..., states={...}]``.
|
||||
|
||||
If there are more than 3 states, only the first 3 states are listed.
|
||||
|
||||
As this class doesn't implement :meth:`object.__str__`, the default implementation
|
||||
will be used, which is equivalent to :meth:`__repr__`.
|
||||
|
||||
Returns:
|
||||
:obj:`str`
|
||||
"""
|
||||
truncation_threshold = 3
|
||||
states = dict(list(self.states.items())[:truncation_threshold])
|
||||
states_string = str(states)
|
||||
if len(self.states) > truncation_threshold:
|
||||
states_string = states_string[:-1] + ", ...}"
|
||||
|
||||
return build_repr_with_selected_attrs(
|
||||
self,
|
||||
name=self.name,
|
||||
states=states_string,
|
||||
)
|
||||
|
||||
@property
|
||||
def entry_points(self) -> List[BaseHandler[Update, CCT]]:
|
||||
"""List[:class:`telegram.ext.BaseHandler`]: A list of :obj:`BaseHandler` objects that can
|
||||
|
||||
+19
-22
@@ -104,6 +104,25 @@ class Defaults:
|
||||
if value is not None:
|
||||
self._api_defaults[kwarg] = value
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(
|
||||
(
|
||||
self._parse_mode,
|
||||
self._disable_notification,
|
||||
self._disable_web_page_preview,
|
||||
self._allow_sending_without_reply,
|
||||
self._quote,
|
||||
self._tzinfo,
|
||||
self._block,
|
||||
self._protect_content,
|
||||
)
|
||||
)
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Defaults):
|
||||
return all(getattr(self, attr) == getattr(other, attr) for attr in self.__slots__)
|
||||
return False
|
||||
|
||||
@property
|
||||
def api_defaults(self) -> Dict[str, Any]: # skip-cq: PY-D0003
|
||||
return self._api_defaults
|
||||
@@ -220,25 +239,3 @@ class Defaults:
|
||||
raise AttributeError(
|
||||
"You can't assign a new value to protect_content after initialization."
|
||||
)
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(
|
||||
(
|
||||
self._parse_mode,
|
||||
self._disable_notification,
|
||||
self._disable_web_page_preview,
|
||||
self._allow_sending_without_reply,
|
||||
self._quote,
|
||||
self._tzinfo,
|
||||
self._block,
|
||||
self._protect_content,
|
||||
)
|
||||
)
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Defaults):
|
||||
return all(getattr(self, attr) == getattr(other, attr) for attr in self.__slots__)
|
||||
return False
|
||||
|
||||
def __ne__(self, other: object) -> bool:
|
||||
return not self == other
|
||||
|
||||
+50
-32
@@ -63,17 +63,14 @@ from telegram import (
|
||||
InlineKeyboardMarkup,
|
||||
InlineQueryResultsButton,
|
||||
InputMedia,
|
||||
InputSticker,
|
||||
Location,
|
||||
MaskPosition,
|
||||
MenuButton,
|
||||
Message,
|
||||
MessageId,
|
||||
PassportElementError,
|
||||
PhotoSize,
|
||||
Poll,
|
||||
SentWebAppMessage,
|
||||
ShippingOption,
|
||||
Sticker,
|
||||
StickerSet,
|
||||
Update,
|
||||
@@ -88,6 +85,7 @@ from telegram import (
|
||||
from telegram._utils.datetime import to_timestamp
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue
|
||||
from telegram._utils.logging import get_logger
|
||||
from telegram._utils.repr import build_repr_with_selected_attrs
|
||||
from telegram._utils.types import (
|
||||
CorrectOptionID,
|
||||
DVInput,
|
||||
@@ -108,8 +106,11 @@ if TYPE_CHECKING:
|
||||
InputMediaDocument,
|
||||
InputMediaPhoto,
|
||||
InputMediaVideo,
|
||||
InputSticker,
|
||||
LabeledPrice,
|
||||
MessageEntity,
|
||||
PassportElementError,
|
||||
ShippingOption,
|
||||
)
|
||||
from telegram.ext import BaseRateLimiter, Defaults
|
||||
|
||||
@@ -246,6 +247,17 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
|
||||
self._callback_data_cache = CallbackDataCache(bot=self, maxsize=maxsize)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Give a string representation of the bot in the form ``ExtBot[token=...]``.
|
||||
|
||||
As this class doesn't implement :meth:`object.__str__`, the default implementation
|
||||
will be used, which is equivalent to :meth:`__repr__`.
|
||||
|
||||
Returns:
|
||||
:obj:`str`
|
||||
"""
|
||||
return build_repr_with_selected_attrs(self, token=self.token)
|
||||
|
||||
@classmethod
|
||||
def _warn(
|
||||
cls, message: str, category: Type[Warning] = PTBUserWarning, stacklevel: int = 0
|
||||
@@ -645,7 +657,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
message_id: int,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -733,9 +745,9 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
|
||||
async def add_sticker_to_set(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
name: str,
|
||||
sticker: Optional[InputSticker],
|
||||
sticker: Optional["InputSticker"],
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = 20,
|
||||
@@ -845,7 +857,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
self,
|
||||
shipping_query_id: str,
|
||||
ok: bool,
|
||||
shipping_options: Optional[Sequence[ShippingOption]] = None,
|
||||
shipping_options: Optional[Sequence["ShippingOption"]] = None,
|
||||
error_message: Optional[str] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -914,7 +926,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
async def ban_chat_member(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
until_date: Optional[Union[int, datetime]] = None,
|
||||
revoke_messages: Optional[bool] = None,
|
||||
*,
|
||||
@@ -1047,10 +1059,10 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
|
||||
async def create_new_sticker_set(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
name: str,
|
||||
title: str,
|
||||
stickers: Optional[Sequence[InputSticker]],
|
||||
stickers: Optional[Sequence["InputSticker"]],
|
||||
sticker_format: Optional[str],
|
||||
sticker_type: Optional[str] = None,
|
||||
needs_repainting: Optional[bool] = None,
|
||||
@@ -1329,7 +1341,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
message_id: Optional[int] = None,
|
||||
inline_message_id: Optional[str] = None,
|
||||
caption: Optional[str] = None,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
*,
|
||||
@@ -1362,7 +1374,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
inline_message_id: Optional[str] = None,
|
||||
latitude: Optional[float] = None,
|
||||
longitude: Optional[float] = None,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
horizontal_accuracy: Optional[float] = None,
|
||||
heading: Optional[int] = None,
|
||||
proximity_alert_radius: Optional[int] = None,
|
||||
@@ -1399,7 +1411,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
chat_id: Optional[Union[str, int]] = None,
|
||||
message_id: Optional[int] = None,
|
||||
inline_message_id: Optional[str] = None,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1455,7 +1467,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
inline_message_id: Optional[str] = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1554,7 +1566,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
async def get_chat_member(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1655,8 +1667,8 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
|
||||
async def get_game_high_scores(
|
||||
self,
|
||||
user_id: Union[int, str],
|
||||
chat_id: Optional[Union[str, int]] = None,
|
||||
user_id: int,
|
||||
chat_id: Optional[int] = None,
|
||||
message_id: Optional[int] = None,
|
||||
inline_message_id: Optional[str] = None,
|
||||
*,
|
||||
@@ -1781,7 +1793,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
|
||||
async def get_user_profile_photos(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
offset: Optional[int] = None,
|
||||
limit: Optional[int] = None,
|
||||
*,
|
||||
@@ -2032,7 +2044,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
async def promote_chat_member(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
can_change_info: Optional[bool] = None,
|
||||
can_post_messages: Optional[bool] = None,
|
||||
can_edit_messages: Optional[bool] = None,
|
||||
@@ -2045,6 +2057,9 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
can_manage_chat: Optional[bool] = None,
|
||||
can_manage_video_chats: Optional[bool] = None,
|
||||
can_manage_topics: Optional[bool] = None,
|
||||
can_post_stories: Optional[bool] = None,
|
||||
can_edit_stories: Optional[bool] = None,
|
||||
can_delete_stories: Optional[bool] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2068,6 +2083,9 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
can_manage_chat=can_manage_chat,
|
||||
can_manage_video_chats=can_manage_video_chats,
|
||||
can_manage_topics=can_manage_topics,
|
||||
can_post_stories=can_post_stories,
|
||||
can_edit_stories=can_edit_stories,
|
||||
can_delete_stories=can_delete_stories,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
@@ -2100,7 +2118,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
async def restrict_chat_member(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
permissions: ChatPermissions,
|
||||
until_date: Optional[Union[int, datetime]] = None,
|
||||
use_independent_chat_permissions: Optional[bool] = None,
|
||||
@@ -2397,11 +2415,11 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
|
||||
async def send_game(
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
chat_id: int,
|
||||
game_short_name: str,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
@@ -2450,7 +2468,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
is_flexible: Optional[bool] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
provider_data: Optional[Union[str, object]] = None,
|
||||
send_phone_number_to_provider: Optional[bool] = None,
|
||||
send_email_to_provider: Optional[bool] = None,
|
||||
@@ -2958,7 +2976,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
async def set_chat_administrator_custom_title(
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
user_id: Union[int, str],
|
||||
user_id: int,
|
||||
custom_title: str,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -3115,9 +3133,9 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
|
||||
async def set_game_score(
|
||||
self,
|
||||
user_id: Union[int, str],
|
||||
user_id: int,
|
||||
score: int,
|
||||
chat_id: Optional[Union[str, int]] = None,
|
||||
chat_id: Optional[int] = None,
|
||||
message_id: Optional[int] = None,
|
||||
inline_message_id: Optional[str] = None,
|
||||
force: Optional[bool] = None,
|
||||
@@ -3193,8 +3211,8 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
|
||||
async def set_passport_data_errors(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
errors: Sequence[PassportElementError],
|
||||
user_id: int,
|
||||
errors: Sequence["PassportElementError"],
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -3238,7 +3256,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
async def set_sticker_set_thumbnail(
|
||||
self,
|
||||
name: str,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
thumbnail: Optional[FileInput] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -3296,7 +3314,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
chat_id: Optional[Union[str, int]] = None,
|
||||
message_id: Optional[int] = None,
|
||||
inline_message_id: Optional[str] = None,
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
reply_markup: Optional["InlineKeyboardMarkup"] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -3320,7 +3338,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
async def unban_chat_member(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
only_if_banned: Optional[bool] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -3449,7 +3467,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||
|
||||
async def upload_sticker_file(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
user_id: int,
|
||||
sticker: Optional[FileInput],
|
||||
sticker_format: Optional[str],
|
||||
*,
|
||||
|
||||
+95
-65
@@ -31,6 +31,7 @@ try:
|
||||
except ImportError:
|
||||
APS_AVAILABLE = False
|
||||
|
||||
from telegram._utils.repr import build_repr_with_selected_attrs
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.ext._extbot import ExtBot
|
||||
@@ -97,6 +98,27 @@ class JobQueue(Generic[CCT]):
|
||||
timezone=pytz.utc, executors={"default": self._executor}
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Give a string representation of the JobQueue in the form ``JobQueue[application=...]``.
|
||||
|
||||
As this class doesn't implement :meth:`object.__str__`, the default implementation
|
||||
will be used, which is equivalent to :meth:`__repr__`.
|
||||
|
||||
Returns:
|
||||
:obj:`str`
|
||||
"""
|
||||
return build_repr_with_selected_attrs(self, application=self.application)
|
||||
|
||||
@property
|
||||
def application(self) -> "Application[Any, CCT, Any, Any, Any, JobQueue[CCT]]":
|
||||
"""The application this JobQueue is associated with."""
|
||||
if self._application is None:
|
||||
raise RuntimeError("No application was set for this JobQueue.")
|
||||
application = self._application()
|
||||
if application is not None:
|
||||
return application
|
||||
raise RuntimeError("The application instance is no longer alive.")
|
||||
|
||||
def _tz_now(self) -> datetime.datetime:
|
||||
return datetime.datetime.now(self.scheduler.timezone)
|
||||
|
||||
@@ -107,14 +129,14 @@ class JobQueue(Generic[CCT]):
|
||||
@overload
|
||||
def _parse_time_input(
|
||||
self,
|
||||
time: Union[float, int, datetime.timedelta, datetime.datetime, datetime.time],
|
||||
time: Union[float, datetime.timedelta, datetime.datetime, datetime.time],
|
||||
shift_day: bool = False,
|
||||
) -> datetime.datetime:
|
||||
...
|
||||
|
||||
def _parse_time_input(
|
||||
self,
|
||||
time: Union[float, int, datetime.timedelta, datetime.datetime, datetime.time, None],
|
||||
time: Union[float, datetime.timedelta, datetime.datetime, datetime.time, None],
|
||||
shift_day: bool = False,
|
||||
) -> Optional[datetime.datetime]:
|
||||
if time is None:
|
||||
@@ -150,16 +172,6 @@ class JobQueue(Generic[CCT]):
|
||||
executors={"default": self._executor},
|
||||
)
|
||||
|
||||
@property
|
||||
def application(self) -> "Application[Any, CCT, Any, Any, Any, JobQueue[CCT]]":
|
||||
"""The application this JobQueue is associated with."""
|
||||
if self._application is None:
|
||||
raise RuntimeError("No application was set for this JobQueue.")
|
||||
application = self._application()
|
||||
if application is not None:
|
||||
return application
|
||||
raise RuntimeError("The application instance is no longer alive.")
|
||||
|
||||
@staticmethod
|
||||
async def job_callback(job_queue: "JobQueue[CCT]", job: "Job[CCT]") -> None:
|
||||
"""This method is used as a callback for the APScheduler jobs.
|
||||
@@ -766,6 +778,40 @@ class Job(Generic[CCT]):
|
||||
|
||||
self._job = cast("APSJob", None) # skipcq: PTC-W0052
|
||||
|
||||
def __getattr__(self, item: str) -> object:
|
||||
try:
|
||||
return getattr(self.job, item)
|
||||
except AttributeError as exc:
|
||||
raise AttributeError(
|
||||
f"Neither 'telegram.ext.Job' nor 'apscheduler.job.Job' has attribute '{item}'"
|
||||
) from exc
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, self.__class__):
|
||||
return self.id == other.id
|
||||
return False
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.id)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Give a string representation of the job in the form
|
||||
``Job[id=..., name=..., callback=..., trigger=...]``.
|
||||
|
||||
As this class doesn't implement :meth:`object.__str__`, the default implementation
|
||||
will be used, which is equivalent to :meth:`__repr__`.
|
||||
|
||||
Returns:
|
||||
:obj:`str`
|
||||
"""
|
||||
return build_repr_with_selected_attrs(
|
||||
self,
|
||||
id=self.job.id,
|
||||
name=self.name,
|
||||
callback=self.callback.__name__,
|
||||
trigger=self.job.trigger,
|
||||
)
|
||||
|
||||
@property
|
||||
def job(self) -> "APSJob":
|
||||
""":class:`apscheduler.job.Job`: The APS Job this job is a wrapper for.
|
||||
@@ -775,46 +821,6 @@ class Job(Generic[CCT]):
|
||||
"""
|
||||
return self._job
|
||||
|
||||
async def run(
|
||||
self, application: "Application[Any, CCT, Any, Any, Any, JobQueue[CCT]]"
|
||||
) -> None:
|
||||
"""Executes the callback function independently of the jobs schedule. Also calls
|
||||
:meth:`telegram.ext.Application.update_persistence`.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
Calls :meth:`telegram.ext.Application.update_persistence`.
|
||||
|
||||
Args:
|
||||
application (:class:`telegram.ext.Application`): The application this job is associated
|
||||
with.
|
||||
"""
|
||||
# We shield the task such that the job isn't cancelled mid-run
|
||||
await asyncio.shield(self._run(application))
|
||||
|
||||
async def _run(
|
||||
self, application: "Application[Any, CCT, Any, Any, Any, JobQueue[CCT]]"
|
||||
) -> None:
|
||||
try:
|
||||
context = application.context_types.context.from_job(self, application)
|
||||
await context.refresh_data()
|
||||
await self.callback(context)
|
||||
except Exception as exc:
|
||||
await application.create_task(
|
||||
application.process_error(None, exc, job=self),
|
||||
name=f"Job:{self.id}:run:process_error",
|
||||
)
|
||||
finally:
|
||||
# This is internal logic of application - let's keep it private for now
|
||||
application._mark_for_persistence_update(job=self) # pylint: disable=protected-access
|
||||
|
||||
def schedule_removal(self) -> None:
|
||||
"""
|
||||
Schedules this job for removal from the :class:`JobQueue`. It will be removed without
|
||||
executing its callback function again.
|
||||
"""
|
||||
self.job.remove()
|
||||
self._removed = True
|
||||
|
||||
@property
|
||||
def removed(self) -> bool:
|
||||
""":obj:`bool`: Whether this job is due to be removed."""
|
||||
@@ -867,18 +873,42 @@ class Job(Generic[CCT]):
|
||||
ext_job._job = aps_job # pylint: disable=protected-access
|
||||
return ext_job
|
||||
|
||||
def __getattr__(self, item: str) -> object:
|
||||
async def run(
|
||||
self, application: "Application[Any, CCT, Any, Any, Any, JobQueue[CCT]]"
|
||||
) -> None:
|
||||
"""Executes the callback function independently of the jobs schedule. Also calls
|
||||
:meth:`telegram.ext.Application.update_persistence`.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
Calls :meth:`telegram.ext.Application.update_persistence`.
|
||||
|
||||
Args:
|
||||
application (:class:`telegram.ext.Application`): The application this job is associated
|
||||
with.
|
||||
"""
|
||||
# We shield the task such that the job isn't cancelled mid-run
|
||||
await asyncio.shield(self._run(application))
|
||||
|
||||
async def _run(
|
||||
self, application: "Application[Any, CCT, Any, Any, Any, JobQueue[CCT]]"
|
||||
) -> None:
|
||||
try:
|
||||
return getattr(self.job, item)
|
||||
except AttributeError as exc:
|
||||
raise AttributeError(
|
||||
f"Neither 'telegram.ext.Job' nor 'apscheduler.job.Job' has attribute '{item}'"
|
||||
) from exc
|
||||
context = application.context_types.context.from_job(self, application)
|
||||
await context.refresh_data()
|
||||
await self.callback(context)
|
||||
except Exception as exc:
|
||||
await application.create_task(
|
||||
application.process_error(None, exc, job=self),
|
||||
name=f"Job:{self.id}:run:process_error",
|
||||
)
|
||||
finally:
|
||||
# This is internal logic of application - let's keep it private for now
|
||||
application._mark_for_persistence_update(job=self) # pylint: disable=protected-access
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, self.__class__):
|
||||
return self.id == other.id
|
||||
return False
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.id)
|
||||
def schedule_removal(self) -> None:
|
||||
"""
|
||||
Schedules this job for removal from the :class:`JobQueue`. It will be removed without
|
||||
executing its callback function again.
|
||||
"""
|
||||
self.job.remove()
|
||||
self._removed = True
|
||||
|
||||
+32
-20
@@ -37,6 +37,7 @@ from typing import (
|
||||
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.logging import get_logger
|
||||
from telegram._utils.repr import build_repr_with_selected_attrs
|
||||
from telegram._utils.types import ODVInput
|
||||
from telegram.error import InvalidToken, RetryAfter, TelegramError, TimedOut
|
||||
|
||||
@@ -124,6 +125,37 @@ class Updater(AsyncContextManager["Updater"]):
|
||||
self.__polling_task: Optional[asyncio.Task] = None
|
||||
self.__polling_cleanup_cb: Optional[Callable[[], Coroutine[Any, Any, None]]] = None
|
||||
|
||||
async def __aenter__(self: _UpdaterType) -> _UpdaterType: # noqa: PYI019
|
||||
"""Simple context manager which initializes the Updater."""
|
||||
try:
|
||||
await self.initialize()
|
||||
return self
|
||||
except Exception as exc:
|
||||
await self.shutdown()
|
||||
raise exc
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
"""Shutdown the Updater from the context manager."""
|
||||
# Make sure not to return `True` so that exceptions are not suppressed
|
||||
# https://docs.python.org/3/reference/datamodel.html?#object.__aexit__
|
||||
await self.shutdown()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Give a string representation of the updater in the form ``Updater[bot=...]``.
|
||||
|
||||
As this class doesn't implement :meth:`object.__str__`, the default implementation
|
||||
will be used, which is equivalent to :meth:`__repr__`.
|
||||
|
||||
Returns:
|
||||
:obj:`str`
|
||||
"""
|
||||
return build_repr_with_selected_attrs(self, bot=self.bot)
|
||||
|
||||
@property
|
||||
def running(self) -> bool:
|
||||
return self._running
|
||||
@@ -163,26 +195,6 @@ class Updater(AsyncContextManager["Updater"]):
|
||||
self._initialized = False
|
||||
_LOGGER.debug("Shut down of Updater complete")
|
||||
|
||||
async def __aenter__(self: _UpdaterType) -> _UpdaterType:
|
||||
"""Simple context manager which initializes the Updater."""
|
||||
try:
|
||||
await self.initialize()
|
||||
return self
|
||||
except Exception as exc:
|
||||
await self.shutdown()
|
||||
raise exc
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
"""Shutdown the Updater from the context manager."""
|
||||
# Make sure not to return `True` so that exceptions are not suppressed
|
||||
# https://docs.python.org/3/reference/datamodel.html?#object.__aexit__
|
||||
await self.shutdown()
|
||||
|
||||
async def start_polling(
|
||||
self,
|
||||
poll_interval: float = 0.0,
|
||||
|
||||
@@ -54,6 +54,14 @@ class TrackingDict(UserDict, Generic[_KT, _VT]):
|
||||
super().__init__()
|
||||
self._write_access_keys: Set[_KT] = set()
|
||||
|
||||
def __setitem__(self, key: _KT, value: _VT) -> None:
|
||||
self.__track_write(key)
|
||||
super().__setitem__(key, value)
|
||||
|
||||
def __delitem__(self, key: _KT) -> None:
|
||||
self.__track_write(key)
|
||||
super().__delitem__(key)
|
||||
|
||||
def __track_write(self, key: Union[_KT, Set[_KT]]) -> None:
|
||||
if isinstance(key, set):
|
||||
self._write_access_keys |= key
|
||||
@@ -83,14 +91,6 @@ class TrackingDict(UserDict, Generic[_KT, _VT]):
|
||||
|
||||
# Override methods to track access
|
||||
|
||||
def __setitem__(self, key: _KT, value: _VT) -> None:
|
||||
self.__track_write(key)
|
||||
super().__setitem__(key, value)
|
||||
|
||||
def __delitem__(self, key: _KT) -> None:
|
||||
self.__track_write(key)
|
||||
super().__delitem__(key)
|
||||
|
||||
def update_no_track(self, mapping: Mapping[_KT, _VT]) -> None:
|
||||
"""Like ``update``, but doesn't count towards write access."""
|
||||
for key, value in mapping.items():
|
||||
|
||||
+34
-34
@@ -182,6 +182,39 @@ class BaseFilter:
|
||||
self._name = self.__class__.__name__ if name is None else name
|
||||
self._data_filter = data_filter
|
||||
|
||||
def __and__(self, other: "BaseFilter") -> "BaseFilter":
|
||||
return _MergedFilter(self, and_filter=other)
|
||||
|
||||
def __or__(self, other: "BaseFilter") -> "BaseFilter":
|
||||
return _MergedFilter(self, or_filter=other)
|
||||
|
||||
def __xor__(self, other: "BaseFilter") -> "BaseFilter":
|
||||
return _XORFilter(self, other)
|
||||
|
||||
def __invert__(self) -> "BaseFilter":
|
||||
return _InvertedFilter(self)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def data_filter(self) -> bool:
|
||||
""":obj:`bool`: Whether this filter is a data filter."""
|
||||
return self._data_filter
|
||||
|
||||
@data_filter.setter
|
||||
def data_filter(self, value: bool) -> None:
|
||||
self._data_filter = value
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
""":obj:`str`: Name for this filter."""
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name: str) -> None:
|
||||
self._name = name
|
||||
|
||||
def check_update( # skipcq: PYL-R0201
|
||||
self, update: Update
|
||||
) -> Optional[Union[bool, FilterDataDict]]:
|
||||
@@ -205,39 +238,6 @@ class BaseFilter:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __and__(self, other: "BaseFilter") -> "BaseFilter":
|
||||
return _MergedFilter(self, and_filter=other)
|
||||
|
||||
def __or__(self, other: "BaseFilter") -> "BaseFilter":
|
||||
return _MergedFilter(self, or_filter=other)
|
||||
|
||||
def __xor__(self, other: "BaseFilter") -> "BaseFilter":
|
||||
return _XORFilter(self, other)
|
||||
|
||||
def __invert__(self) -> "BaseFilter":
|
||||
return _InvertedFilter(self)
|
||||
|
||||
@property
|
||||
def data_filter(self) -> bool:
|
||||
""":obj:`bool`: Whether this filter is a data filter."""
|
||||
return self._data_filter
|
||||
|
||||
@data_filter.setter
|
||||
def data_filter(self, value: bool) -> None:
|
||||
self._data_filter = value
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
""":obj:`str`: Name for this filter."""
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name: str) -> None:
|
||||
self._name = name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.name
|
||||
|
||||
|
||||
class MessageFilter(BaseFilter):
|
||||
"""Base class for all Message Filters. In contrast to :class:`UpdateFilter`, the object passed
|
||||
@@ -763,7 +763,7 @@ class _ChatUserBaseFilter(MessageFilter, ABC):
|
||||
def name(self) -> str:
|
||||
return (
|
||||
f"filters.{self.__class__.__name__}("
|
||||
f'{", ".join(str(s) for s in (self.usernames or self.chat_ids))})'
|
||||
f"{', '.join(str(s) for s in (self.usernames or self.chat_ids))})"
|
||||
)
|
||||
|
||||
@name.setter
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
#! /usr/bin/env python
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2023
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ complete and correct. To run it, export an environment variable first:
|
||||
|
||||
$ export TEST_OFFICIAL=true
|
||||
|
||||
and then run ``pytest tests/test_official.py``.
|
||||
and then run ``pytest tests/test_official.py``. Note: You need py 3.10+ to run this test.
|
||||
|
||||
We also have another marker, ``@pytest.mark.dev``, which you can add to tests that you want to run selectively.
|
||||
Use as follows:
|
||||
|
||||
@@ -99,7 +99,9 @@ class TestPhotoWithoutRequest(TestPhotoBase):
|
||||
assert photo.file_size in self.file_size
|
||||
assert thumb.width == 90
|
||||
assert thumb.height == 90
|
||||
assert thumb.file_size == 1477
|
||||
# File sizes don't seem to be consistent, so we use the values that we have observed
|
||||
# so far
|
||||
assert thumb.file_size in [1475, 1477]
|
||||
|
||||
def test_de_json(self, bot, photo):
|
||||
json_dict = {
|
||||
|
||||
@@ -103,7 +103,7 @@ class TestStickerBase:
|
||||
file_size = 39518
|
||||
thumb_width = 319
|
||||
thumb_height = 320
|
||||
thumb_file_size = 21472
|
||||
thumb_file_size = 21448
|
||||
type = Sticker.REGULAR
|
||||
custom_emoji_id = "ThisIsSuchACustomEmojiID"
|
||||
needs_repainting = True
|
||||
|
||||
@@ -36,12 +36,16 @@ def inline_keyboard_button():
|
||||
url=TestInlineKeyboardButtonBase.url,
|
||||
callback_data=TestInlineKeyboardButtonBase.callback_data,
|
||||
switch_inline_query=TestInlineKeyboardButtonBase.switch_inline_query,
|
||||
switch_inline_query_current_chat=TestInlineKeyboardButtonBase.switch_inline_query_current_chat, # noqa: E501
|
||||
switch_inline_query_current_chat=(
|
||||
TestInlineKeyboardButtonBase.switch_inline_query_current_chat
|
||||
),
|
||||
callback_game=TestInlineKeyboardButtonBase.callback_game,
|
||||
pay=TestInlineKeyboardButtonBase.pay,
|
||||
login_url=TestInlineKeyboardButtonBase.login_url,
|
||||
web_app=TestInlineKeyboardButtonBase.web_app,
|
||||
switch_inline_query_chosen_chat=TestInlineKeyboardButtonBase.switch_inline_query_chosen_chat, # noqa: E501
|
||||
switch_inline_query_chosen_chat=(
|
||||
TestInlineKeyboardButtonBase.switch_inline_query_chosen_chat
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -43,7 +43,9 @@ def input_invoice_message_content():
|
||||
need_phone_number=TestInputInvoiceMessageContentBase.need_phone_number,
|
||||
need_email=TestInputInvoiceMessageContentBase.need_email,
|
||||
need_shipping_address=TestInputInvoiceMessageContentBase.need_shipping_address,
|
||||
send_phone_number_to_provider=TestInputInvoiceMessageContentBase.send_phone_number_to_provider, # noqa: E501
|
||||
send_phone_number_to_provider=(
|
||||
TestInputInvoiceMessageContentBase.send_phone_number_to_provider
|
||||
),
|
||||
send_email_to_provider=TestInputInvoiceMessageContentBase.send_email_to_provider,
|
||||
is_flexible=TestInputInvoiceMessageContentBase.is_flexible,
|
||||
)
|
||||
|
||||
@@ -468,7 +468,11 @@ class TestPassportWithoutRequest(TestPassportBase):
|
||||
assert passport_data.decrypted_data
|
||||
|
||||
async def test_wrong_key(self, bot):
|
||||
short_key = b"-----BEGIN RSA PRIVATE KEY-----\r\nMIIBOQIBAAJBAKU+OZ2jJm7sCA/ec4gngNZhXYPu+DZ/TAwSMl0W7vAPXAsLplBk\r\nO8l6IBHx8N0ZC4Bc65mO3b2G8YAzqndyqH8CAwEAAQJAWOx3jQFzeVXDsOaBPdAk\r\nYTncXVeIc6tlfUl9mOLyinSbRNCy1XicOiOZFgH1rRKOGIC1235QmqxFvdecySoY\r\nwQIhAOFeGgeX9CrEPuSsd9+kqUcA2avCwqdQgSdy2qggRFyJAiEAu7QHT8JQSkHU\r\nDELfzrzc24AhjyG0z1DpGZArM8COascCIDK42SboXj3Z2UXiQ0CEcMzYNiVgOisq\r\nBUd5pBi+2mPxAiAM5Z7G/Sv1HjbKrOGh29o0/sXPhtpckEuj5QMC6E0gywIgFY6S\r\nNjwrAA+cMmsgY0O2fAzEKkDc5YiFsiXaGaSS4eA=\r\n-----END RSA PRIVATE KEY-----"
|
||||
short_key = (
|
||||
b"-----BEGIN RSA PRIVATE"
|
||||
b" KEY-----\r\nMIIBOQIBAAJBAKU+OZ2jJm7sCA/ec4gngNZhXYPu+DZ/TAwSMl0W7vAPXAsLplBk\r\nO8l6IBHx8N0ZC4Bc65mO3b2G8YAzqndyqH8CAwEAAQJAWOx3jQFzeVXDsOaBPdAk\r\nYTncXVeIc6tlfUl9mOLyinSbRNCy1XicOiOZFgH1rRKOGIC1235QmqxFvdecySoY\r\nwQIhAOFeGgeX9CrEPuSsd9+kqUcA2avCwqdQgSdy2qggRFyJAiEAu7QHT8JQSkHU\r\nDELfzrzc24AhjyG0z1DpGZArM8COascCIDK42SboXj3Z2UXiQ0CEcMzYNiVgOisq\r\nBUd5pBi+2mPxAiAM5Z7G/Sv1HjbKrOGh29o0/sXPhtpckEuj5QMC6E0gywIgFY6S\r\nNjwrAA+cMmsgY0O2fAzEKkDc5YiFsiXaGaSS4eA=\r\n-----END"
|
||||
b" RSA PRIVATE KEY-----"
|
||||
)
|
||||
async with make_bot(token=bot.token, private_key=short_key) as b:
|
||||
passport_data = PassportData.de_json(RAW_PASSPORT_DATA, bot=b)
|
||||
with pytest.raises(PassportDecryptionError):
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import pytest
|
||||
|
||||
from telegram import PassportElementErrorFiles, PassportElementErrorSelfie
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@@ -58,11 +59,11 @@ class TestPassportElementErrorFilesWithoutRequest(TestPassportElementErrorFilesB
|
||||
assert isinstance(passport_element_error_files_dict, dict)
|
||||
assert passport_element_error_files_dict["source"] == passport_element_error_files.source
|
||||
assert passport_element_error_files_dict["type"] == passport_element_error_files.type
|
||||
assert passport_element_error_files_dict["message"] == passport_element_error_files.message
|
||||
assert (
|
||||
passport_element_error_files_dict["file_hashes"]
|
||||
== passport_element_error_files.file_hashes
|
||||
)
|
||||
assert passport_element_error_files_dict["message"] == passport_element_error_files.message
|
||||
|
||||
def test_equality(self):
|
||||
a = PassportElementErrorFiles(self.type_, self.file_hashes, self.message)
|
||||
@@ -87,3 +88,13 @@ class TestPassportElementErrorFilesWithoutRequest(TestPassportElementErrorFilesB
|
||||
|
||||
assert a != f
|
||||
assert hash(a) != hash(f)
|
||||
|
||||
def test_file_hashes_deprecated(self, passport_element_error_files, recwarn):
|
||||
passport_element_error_files.file_hashes
|
||||
assert len(recwarn) == 1
|
||||
assert (
|
||||
"The attribute `file_hashes` will return a tuple instead of a list in future major"
|
||||
" versions." in str(recwarn[0].message)
|
||||
)
|
||||
assert recwarn[0].category is PTBDeprecationWarning
|
||||
assert recwarn[0].filename == __file__
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import pytest
|
||||
|
||||
from telegram import PassportElementErrorSelfie, PassportElementErrorTranslationFiles
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@@ -68,14 +69,14 @@ class TestPassportElementErrorTranslationFilesWithoutRequest(
|
||||
passport_element_error_translation_files_dict["type"]
|
||||
== passport_element_error_translation_files.type
|
||||
)
|
||||
assert (
|
||||
passport_element_error_translation_files_dict["file_hashes"]
|
||||
== passport_element_error_translation_files.file_hashes
|
||||
)
|
||||
assert (
|
||||
passport_element_error_translation_files_dict["message"]
|
||||
== passport_element_error_translation_files.message
|
||||
)
|
||||
assert (
|
||||
passport_element_error_translation_files_dict["file_hashes"]
|
||||
== passport_element_error_translation_files.file_hashes
|
||||
)
|
||||
|
||||
def test_equality(self):
|
||||
a = PassportElementErrorTranslationFiles(self.type_, self.file_hashes, self.message)
|
||||
@@ -100,3 +101,13 @@ class TestPassportElementErrorTranslationFilesWithoutRequest(
|
||||
|
||||
assert a != f
|
||||
assert hash(a) != hash(f)
|
||||
|
||||
def test_file_hashes_deprecated(self, passport_element_error_translation_files, recwarn):
|
||||
passport_element_error_translation_files.file_hashes
|
||||
assert len(recwarn) == 1
|
||||
assert (
|
||||
"The attribute `file_hashes` will return a tuple instead of a list in future major"
|
||||
" versions." in str(recwarn[0].message)
|
||||
)
|
||||
assert recwarn[0].category is PTBDeprecationWarning
|
||||
assert recwarn[0].filename == __file__
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import pytest
|
||||
|
||||
from telegram import Bot, File, PassportElementError, PassportFile
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
from tests.auxil.bot_method_checks import (
|
||||
check_defaults_handling,
|
||||
check_shortcut_call,
|
||||
@@ -88,6 +89,16 @@ class TestPassportFileWithoutRequest(TestPassportFileBase):
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
def test_file_date_deprecated(self, passport_file, recwarn):
|
||||
passport_file.file_date
|
||||
assert len(recwarn) == 1
|
||||
assert (
|
||||
"The attribute `file_date` will return a datetime instead of an integer in future"
|
||||
" major versions." in str(recwarn[0].message)
|
||||
)
|
||||
assert recwarn[0].category is PTBDeprecationWarning
|
||||
assert recwarn[0].filename == __file__
|
||||
|
||||
async def test_get_file_instance_method(self, monkeypatch, passport_file):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
result = kwargs["file_id"] == passport_file.file_id
|
||||
|
||||
@@ -301,8 +301,10 @@ class TestInvoiceWithRequest(TestInvoiceBase):
|
||||
suggested_tip_amounts=self.suggested_tip_amounts,
|
||||
start_parameter=self.start_parameter,
|
||||
provider_data=self.provider_data,
|
||||
photo_url="https://raw.githubusercontent.com/"
|
||||
"python-telegram-bot/logos/master/logo/png/ptb-logo_240.png",
|
||||
photo_url=(
|
||||
"https://raw.githubusercontent.com/"
|
||||
"python-telegram-bot/logos/master/logo/png/ptb-logo_240.png"
|
||||
),
|
||||
photo_size=240,
|
||||
photo_width=240,
|
||||
photo_height=240,
|
||||
|
||||
@@ -29,3 +29,4 @@ def env_var_2_bool(env_var: object) -> bool:
|
||||
|
||||
GITHUB_ACTION = os.getenv("GITHUB_ACTION", "")
|
||||
TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", "true"))
|
||||
RUN_TEST_OFFICIAL = env_var_2_bool(os.getenv("TEST_OFFICIAL"))
|
||||
|
||||
+11
-1
@@ -18,6 +18,7 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import asyncio
|
||||
import datetime
|
||||
import logging
|
||||
import sys
|
||||
from typing import Dict, List
|
||||
from uuid import uuid4
|
||||
@@ -40,7 +41,7 @@ from telegram.ext.filters import MessageFilter, UpdateFilter
|
||||
from tests.auxil.build_messages import DATE
|
||||
from tests.auxil.ci_bots import BOT_INFO_PROVIDER
|
||||
from tests.auxil.constants import PRIVATE_KEY
|
||||
from tests.auxil.envvars import TEST_WITH_OPT_DEPS
|
||||
from tests.auxil.envvars import RUN_TEST_OFFICIAL, TEST_WITH_OPT_DEPS
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.networking import NonchalantHttpxRequest
|
||||
from tests.auxil.pytest_classes import PytestApplication, PytestBot, make_bot
|
||||
@@ -50,6 +51,15 @@ if TEST_WITH_OPT_DEPS:
|
||||
import pytz
|
||||
|
||||
|
||||
# Don't collect `test_official.py` on Python 3.10- since it uses newer features like X | Y syntax.
|
||||
# Docs: https://docs.pytest.org/en/7.1.x/example/pythoncollection.html#customizing-test-collection
|
||||
collect_ignore = []
|
||||
if sys.version_info < (3, 10):
|
||||
if RUN_TEST_OFFICIAL:
|
||||
logging.warning("Skipping test_official.py since it requires Python 3.10+")
|
||||
collect_ignore.append("test_official.py")
|
||||
|
||||
|
||||
# This is here instead of in setup.cfg due to https://github.com/pytest-dev/pytest/issues/8343
|
||||
def pytest_runtestloop(session: pytest.Session):
|
||||
session.add_marker(
|
||||
|
||||
@@ -201,6 +201,9 @@ class TestApplication:
|
||||
assert isinstance(app.chat_data[1], dict)
|
||||
assert isinstance(app.user_data[1], dict)
|
||||
|
||||
async def test_repr(self, app):
|
||||
assert repr(app) == f"PytestApplication[bot={app.bot!r}]"
|
||||
|
||||
def test_job_queue(self, one_time_bot, app, recwarn):
|
||||
expected_warning = (
|
||||
"No `JobQueue` set up. To use `JobQueue`, you must install PTB via "
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user