Compare commits

..

45 Commits

Author SHA1 Message Date
Hinrich Mahler a8f1164b0c Bump Version to v20.7 2023-11-27 19:01:57 +01:00
Bibo-Joshi da11561f87 Adjust read_timeout Behavior for Bot.get_updates (#3963) 2023-11-27 18:24:21 +01:00
Bibo-Joshi 354a8e0854 Improve write_timeout Handling for Media Methods (#3952) 2023-11-26 16:44:18 +01:00
dependabot[bot] bc68488c14 Update httpx requirement from ~=0.25.1 to ~=0.25.2 (#3983)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
2023-11-25 06:46:55 +01:00
dependabot[bot] 19d7939355 Bump pytest-xdist from 3.4.0 to 3.5.0 (#3982)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-25 06:26:30 +01:00
dependabot[bot] 3495ce3aeb Bump pytest-xdist from 3.3.1 to 3.4.0 (#3975)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-22 22:21:39 +01:00
pre-commit-ci[bot] dd9af64a5c [pre-commit.ci] pre-commit autoupdate (#3967)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
2023-11-12 10:11:22 +01:00
Bibo-Joshi da3bc6974a Adjust Tests to New Error Messages (#3970) 2023-11-11 11:35:23 +01:00
Bibo-Joshi b1fc0596b9 Fix Persistency Issue with Ended Non-Blocking Conversations (#3962) 2023-11-05 11:48:44 +01:00
Bibo-Joshi 6d2334c88b Improve Insertion of Kwargs into Bot Methods (#3965) 2023-11-05 11:47:50 +01:00
dependabot[bot] a0c81ec3d4 Update httpx requirement from ~=0.25.0 to ~=0.25.1 (#3961)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <dependabot[bot]@users.noreply.github.com>
2023-11-04 08:34:31 +01:00
dependabot[bot] c8d9898eaa Bump srvaroa/labeler from 1.6.1 to 1.7.0 (#3958)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-03 23:38:03 +01:00
Bibo-Joshi 616b0b55ef Add ApplicationBuilder.(get_updates_)socket_options (#3943) 2023-10-31 16:27:30 +01:00
dependabot[bot] c71612ffae Update cachetools requirement from ~=5.3.1 to ~=5.3.2 (#3954)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
2023-10-28 10:30:45 +02:00
dependabot[bot] 4143d99f56 Bump pytest from 7.4.2 to 7.4.3 (#3953)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-28 10:01:21 +02:00
Harshil cbe808e471 Improve Type Hinting for Arguments with Default Values in Bot (#3942) 2023-10-25 21:53:43 +02:00
Bibo-Joshi 300ec920a1 Add filters.Mention (#3941)
Co-authored-by: Javohir Elmurodov <elmurodovjavohir@gmail.com>
2023-10-23 21:11:56 +02:00
Bibo-Joshi 075f517458 Rename proxy_url to proxy and Allow httpx.{Proxy, URL} as Input (#3939) 2023-10-23 21:09:28 +02:00
Bibo-Joshi c82a0808d1 Improve BaseHandler.__repr__ for Callbacks without __qualname__ (#3934) 2023-10-22 12:43:23 +02:00
Bibo-Joshi ea7e5a69aa Add Parameter socket_options to HTTPXRequest (#3935) 2023-10-22 12:42:22 +02:00
Bibo-Joshi f67e8c0804 Add JobQueue.scheduler_configuration and Corresponding Warnings (#3913) 2023-10-16 20:25:25 +02:00
Aditya Yadav af130ef5e7 Add Documentation for __aenter__ and __aexit__ Methods (#3907)
Co-authored-by: Aditya <clot27@apx_managed.vanilla>
2023-10-09 18:59:52 +02:00
Hinrich Mahler 9ef8826f33 Bump Version to v20.6 2023-10-03 15:39:57 +02:00
Bibo-Joshi 61b70efb4c Drop Backward Compatibility Layer Introduced in #3853 (API 6.8) (#3873) 2023-10-03 15:15:17 +02:00
Bibo-Joshi 63977ea353 Documentation Improvements (#3910)
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
2023-10-03 15:11:37 +02:00
Harshil ae57d3b7c3 API 6.9 (#3898)
Co-authored-by: poolitzer <github@poolitzer.eu>
Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
2023-10-03 14:32:57 +02:00
Harshil 8d76087bed Update pre-commit Dependencies (#3916) 2023-10-03 14:17:42 +02:00
Harshil 0e90deafb5 Add Support Python 3.12 (#3915) 2023-10-03 14:01:06 +02:00
Bibo-Joshi 39d45124df Add Rich Equality Comparison to WriteAccessAllowed (#3911) 2023-10-02 20:21:51 +02:00
dependabot[bot] 895403a0b5 Bump actions/checkout from 3 to 4 (#3914)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-02 20:20:28 +02:00
Bibo-Joshi 8cb177cb2c Move Bot API Tests to Separate Workflow File (#3912)
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
2023-10-02 19:51:34 +02:00
Bibo-Joshi eaf802e07d Fix Failing file_size Tests (#3906) 2023-10-01 11:30:56 +02:00
Dmitry K 1ef242a17e Add __repr__ Methods Added in #3826 to Sphinx Documentation (#3901) 2023-09-26 19:18:12 +02:00
dependabot[bot] 7adb4fa2db Update httpx requirement from ~=0.24.1 to ~=0.25.0 (#3891)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 08:52:10 +02:00
dependabot[bot] 5c5ee598a2 Bump furo from 2023.8.19 to 2023.9.10 (#3890)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-22 19:44:33 +02:00
dependabot[bot] a4ae6f2097 Bump sphinx from 7.2.5 to 7.2.6 (#3892)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
2023-09-22 18:53:07 +02:00
dependabot[bot] fc5a56c15b Update tornado requirement from ~=6.2 to ~=6.3.3 (#3675)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
2023-09-22 18:47:50 +02:00
Bibo-Joshi 74112bfd06 Set Threshold for DeepSource's PY-R1000 to High (#3888) 2023-09-22 18:39:26 +02:00
Harshil ab90cd7359 One-Time Code Formatting Improvement via --preview Flag of black (#3882) 2023-09-22 18:19:21 +02:00
Harshil 5b0f1697f1 Move Dunder Methods to the Top of Class Bodies (#3883)
Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
2023-09-15 22:19:45 +02:00
Dmitry K 9c7298c17a Add String Representation for Selected Classes (#3826) 2023-09-15 21:35:45 +02:00
Harshil 39abf838fa Verify Type Hints for Bot Method & Telegram Class Parameters (#3868) 2023-09-15 21:33:42 +02:00
Harshil 04b44f4595 Remove Superfluous Defaults.__ne__ (#3884) 2023-09-11 21:12:20 +02:00
dependabot[bot] a0decdac28 Bump pytest from 7.4.0 to 7.4.2 (#3881)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-10 12:30:55 +02:00
pre-commit-ci[bot] f77f4b0cf7 pre-commit autoupdate (#3876)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
2023-09-09 23:12:30 +02:00
135 changed files with 3372 additions and 1385 deletions
+1
View File
@@ -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"
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
pull-requests: write # for srvaroa/labeler to add labels in PR
runs-on: ubuntu-latest
steps:
- uses: srvaroa/labeler@v1.6.1
- uses: srvaroa/labeler@v1.7.0
# Config file at .github/labeler.yml
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+47
View File
@@ -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
+1 -1
View File
@@ -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
+16 -16
View File
@@ -6,7 +6,7 @@ ci:
repos:
- repo: https://github.com/psf/black
rev: 23.7.0
rev: 23.10.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.1
hooks:
- id: pylint
files: ^(telegram|examples)/.*\.py$
@@ -28,14 +28,14 @@ repos:
- --jobs=0
additional_dependencies:
- httpx~=0.24.1
- tornado~=6.2
- httpx~=0.25.2
- tornado~=6.3.3
- APScheduler~=3.10.4
- cachetools~=5.3.1
- cachetools~=5.3.2
- aiolimiter~=1.1.0
- . # this basically does `pip install -e .`
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.4.1
rev: v1.6.1
hooks:
- id: mypy
name: mypy-ptb
@@ -44,10 +44,10 @@ repos:
- types-pytz
- types-cryptography
- types-cachetools
- httpx~=0.24.1
- tornado~=6.2
- httpx~=0.25.2
- tornado~=6.3.3
- APScheduler~=3.10.4
- cachetools~=5.3.1
- cachetools~=5.3.2
- aiolimiter~=1.1.0
- . # this basically does `pip install -e .`
- id: mypy
@@ -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
- cachetools~=5.3.2
- . # this basically does `pip install -e .`
- repo: https://github.com/asottile/pyupgrade
rev: v3.10.1
rev: v3.15.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.1.5'
hooks:
- id: ruff
name: ruff
files: ^(telegram|examples|tests)/.*\.py$
additional_dependencies:
- httpx~=0.24.1
- tornado~=6.2
- httpx~=0.25.2
- tornado~=6.3.3
- APScheduler~=3.10.4
- cachetools~=5.3.1
- cachetools~=5.3.2
- aiolimiter~=1.1.0
+99
View File
@@ -4,6 +4,105 @@
Changelog
=========
Version 20.6
============
*Released 2023-11-27*
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>`__.
New Features
------------
- Add ``JobQueue.scheduler_configuration`` and Corresponding Warnings (:pr:`3913` closes :issue:`3837`)
- Add Parameter ``socket_options`` to ``HTTPXRequest`` (:pr:`3935` closes :issue:`2965`)
- Add ``ApplicationBuilder.(get_updates_)socket_options`` (:pr:`3943`)
- Improve ``write_timeout`` Handling for Media Methods (:pr:`3952`)
- Add ``filters.Mention`` (:pr:`3941` closes :issue:`3799`)
- Rename ``proxy_url`` to ``proxy`` and Allow ``httpx.{Proxy, URL}`` as Input (:pr:`3939` closes :issue:`3844`)
Bug Fixes & Changes
-------------------
- Adjust ``read_timeout`` Behavior for ``Bot.get_updates`` (:pr:`3963` closes :issue:`3893`)
- Improve ``BaseHandler.__repr__`` for Callbacks without ``__qualname__`` (:pr:`3934`)
- Fix Persistency Issue with Ended Non-Blocking Conversations (:pr:`3962`)
- Improve Type Hinting for Arguments with Default Values in ``Bot`` (:pr:`3942`)
Documentation Improvements
--------------------------
- Add Documentation for ``__aenter__`` and ``__aexit__`` Methods (:pr:`3907` closes :issue:`3886`)
- Improve Insertion of Kwargs into ``Bot`` Methods (:pr:`3965`)
Internal Changes
----------------
- Adjust Tests to New Error Messages (:pr:`3970`)
Dependency Updates
------------------
- Bump ``pytest-xdist`` from 3.3.1 to 3.4.0 (:pr:`3975`)
- ``pre-commit`` autoupdate (:pr:`3967`)
- Update ``httpx`` requirement from ~=0.25.1 to ~=0.25.2 (:pr:`3983`)
- Bump ``pytest-xdist`` from 3.4.0 to 3.5.0 (:pr:`3982`)
- Update ``httpx`` requirement from ~=0.25.0 to ~=0.25.1 (:pr:`3961`)
- Bump ``srvaroa/labeler`` from 1.6.1 to 1.7.0 (:pr:`3958`)
- Update ``cachetools`` requirement from ~=5.3.1 to ~=5.3.2 (:pr:`3954`)
- Bump ``pytest`` from 7.4.2 to 7.4.3 (:pr:`3953`)
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 DeepSources 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*
+5 -5
View File
@@ -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
==========
@@ -135,7 +135,7 @@ As these features are *optional*, the corresponding 3rd party dependencies are n
Instead, they are listed as optional dependencies.
This allows to avoid unnecessary dependency conflicts for users who don't need the optional features.
The only required dependency is `httpx ~= 0.24.1 <https://www.python-httpx.org>`_ for
The only required dependency is `httpx ~= 0.25.2 <https://www.python-httpx.org>`_ for
``telegram.request.HTTPXRequest``, the default networking backend.
``python-telegram-bot`` is most useful when used along with additional libraries.
@@ -152,8 +152,8 @@ 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[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[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.2 <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``.
To install multiple optional dependencies, separate them by commas, e.g. ``pip install "python-telegram-bot[socks,webhooks]"``.
+3 -3
View File
@@ -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
==========
@@ -136,7 +136,7 @@ As these features are *optional*, the corresponding 3rd party dependencies are n
Instead, they are listed as optional dependencies.
This allows to avoid unnecessary dependency conflicts for users who don't need the optional features.
The only required dependency is `httpx ~= 0.24.1 <https://www.python-httpx.org>`_ for
The only required dependency is `httpx ~= 0.25.2 <https://www.python-httpx.org>`_ for
``telegram.request.HTTPXRequest``, the default networking backend.
``python-telegram-bot`` is most useful when used along with additional libraries.
+4 -4
View File
@@ -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)}"
)
+61 -32
View File
@@ -18,51 +18,80 @@
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}.",
":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.",
":kwtype _sphinx_paramlinks_telegram.Bot.{method}.api_kwargs: :obj:`dict`, optional",
"Keyword Arguments:",
(
" read_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to "
" :paramref:`telegram.request.BaseRequest.post.read_timeout`. Defaults to "
" :attr:`~telegram.request.BaseRequest.DEFAULT_NONE`. "
),
(
" write_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to "
" :paramref:`telegram.request.BaseRequest.post.write_timeout`. Defaults to "
" :attr:`~telegram.request.BaseRequest.DEFAULT_NONE`."
),
(
" connect_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to "
" :paramref:`telegram.request.BaseRequest.post.connect_timeout`. Defaults to "
" :attr:`~telegram.request.BaseRequest.DEFAULT_NONE`."
),
(
" pool_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to "
" :paramref:`telegram.request.BaseRequest.post.pool_timeout`. Defaults to "
" :attr:`~telegram.request.BaseRequest.DEFAULT_NONE`."
),
(
" api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments"
" to be passed to the Telegram API."
),
"",
]
write_timeout_sub = [":attr:`~telegram.request.BaseRequest.DEFAULT_NONE`", "``20``"]
read_timeout_sub = [
":attr:`~telegram.request.BaseRequest.DEFAULT_NONE`",
"``2``. :paramref:`timeout` will be added to this value",
media_write_timeout_deprecation_methods = [
"send_photo",
"send_audio",
"send_document",
"send_sticker",
"send_video",
"send_video_note",
"send_animation",
"send_voice",
"send_media_group",
"set_chat_photo",
"upload_sticker_file",
"add_sticker_to_set",
"create_new_sticker_set",
]
media_write_timeout_deprecation = [
" write_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to "
" :paramref:`telegram.request.BaseRequest.post.write_timeout`. By default, ``20`` "
" seconds are used as write timeout."
"",
"",
" .. deprecated:: 20.7",
" In future versions, the default value will be changed to "
" :attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.",
"",
"",
]
get_updates_read_timeout_addition = [
" :paramref:`timeout` will be added to this value.",
"",
"",
" .. versionchanged:: 20.7",
" Defaults to :attr:`~telegram.request.BaseRequest.DEFAULT_NONE` instead of ",
" ``2``.",
]
read_timeout_type = [":obj:`float` | :obj:`None`", ":obj:`float`"]
def find_insert_pos_for_kwargs(lines: list[str]) -> int:
"""Finds the correct position to insert the keyword arguments and returns the index."""
for idx, value in reversed(list(enumerate(lines))): # reversed since :returns: is at the end
if value.startswith(":returns:"):
if value.startswith("Returns"):
return idx
else:
return False
def is_write_timeout_20(obj: object) -> int:
"""inspects the default value of write_timeout parameter of the bot method."""
sig = inspect.signature(obj)
return 1 if (sig.parameters["write_timeout"].default == 20) else 0
def check_timeout_and_api_kwargs_presence(obj: object) -> int:
"""Checks if the method has timeout and api_kwargs keyword only parameters."""
sig = inspect.signature(obj)
+19 -15
View File
@@ -29,11 +29,10 @@ from docs.auxil.admonition_inserter import AdmonitionInserter
from docs.auxil.kwargs_insertion import (
check_timeout_and_api_kwargs_presence,
find_insert_pos_for_kwargs,
is_write_timeout_20,
get_updates_read_timeout_addition,
keyword_args,
read_timeout_sub,
read_timeout_type,
write_timeout_sub,
media_write_timeout_deprecation,
media_write_timeout_deprecation_methods,
)
from docs.auxil.link_code import LINE_NUMBERS
@@ -107,19 +106,24 @@ def autodoc_process_docstring(
f"Couldn't find the correct position to insert the keyword args for {obj}."
)
long_write_timeout = is_write_timeout_20(obj)
get_updates_sub = 1 if (method_name == "get_updates") else 0
get_updates: bool = method_name == "get_updates"
# The below can be done in 1 line with itertools.chain, but this must be modified in-place
insert_idx = insert_index
for i in range(insert_index, insert_index + len(keyword_args)):
lines.insert(
i,
keyword_args[i - insert_index].format(
method=method_name,
write_timeout=write_timeout_sub[long_write_timeout],
read_timeout=read_timeout_sub[get_updates_sub],
read_timeout_type=read_timeout_type[get_updates_sub],
),
)
to_insert = keyword_args[i - insert_index]
if (
"post.write_timeout`. Defaults to" in to_insert
and method_name in media_write_timeout_deprecation_methods
):
effective_insert: list[str] = media_write_timeout_deprecation
elif get_updates and to_insert.lstrip().startswith("read_timeout"):
effective_insert = [to_insert] + get_updates_read_timeout_addition
else:
effective_insert = [to_insert]
lines[insert_idx:insert_idx] = effective_insert
insert_idx += len(effective_insert)
ADMONITION_INSERTER.insert_admonitions(
obj=typing.cast(collections.abc.Callable, obj),
+2 -2
View File
@@ -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
+72 -53
View File
@@ -21,9 +21,9 @@ author = "Leandro Toledo"
# built documents.
#
# The short X.Y version.
version = "20.5" # telegram.__version__[:3]
version = "20.7" # telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = "20.5" # telegram.__version__
release = "20.7" # telegram.__version__
# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = "6.1.3"
@@ -77,6 +77,12 @@ napoleon_use_admonition_for_examples = True
# and we document the types anyway
autodoc_typehints = "none"
# Show docstring for special members
autodoc_default_options = {
"special-members": True,
"exclude-members": "__init__",
}
# Fail on warnings & unresolved references etc
nitpicky = True
@@ -96,7 +102,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 +139,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": "",
},
],
@@ -304,5 +320,8 @@ from docs.auxil.tg_const_role import CONSTANTS_ROLE, TGConstXRefRole
def setup(app: Sphinx):
app.connect("autodoc-skip-member", autodoc_skip_member)
app.connect("autodoc-process-bases", autodoc_process_bases)
app.connect("autodoc-process-docstring", autodoc_process_docstring)
# The default priority is 500. We want our function to run before napoleon doc-conversion
# and sphinx-paramlinks do, b/c otherwise the inserted kwargs in the bot methods won't show
# up in the objects.inv file that Sphinx generates (i.e. not in the search).
app.connect("autodoc-process-docstring", autodoc_process_docstring, priority=100)
app.add_role_to_domain("py", CONSTANTS_ROLE, TGConstXRefRole())
+1 -1
View File
@@ -4,4 +4,4 @@ Bot
.. autoclass:: telegram.Bot
:members:
:show-inheritance:
:special-members: __reduce__, __deepcopy__
:special-members: __repr__, __reduce__, __deepcopy__
+1
View File
@@ -4,3 +4,4 @@ Application
.. autoclass:: telegram.ext.Application
:members:
:show-inheritance:
:special-members: __repr__
+1
View File
@@ -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__
+1
View File
@@ -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__
+1 -1
View File
@@ -4,4 +4,4 @@ Job
.. autoclass:: telegram.ext.Job
:members:
:show-inheritance:
:special-members: __call__
:special-members: __call__, __repr__
+1
View File
@@ -4,3 +4,4 @@ JobQueue
.. autoclass:: telegram.ext.JobQueue
:members:
:show-inheritance:
:special-members: __repr__
+1
View File
@@ -4,3 +4,4 @@ Updater
.. autoclass:: telegram.ext.Updater
:members:
:show-inheritance:
:special-members: __repr__
+2
View File
@@ -61,3 +61,5 @@
.. |removed_thumb_url_note| replace:: Removed the deprecated argument and attribute ``thumb_url``.
.. |removed_thumb_wildcard_note| replace:: Removed the deprecated arguments and attributes ``thumb_*``.
.. |async_context_manager| replace:: Asynchronous context manager which
+1 -1
View File
@@ -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 -1
View File
@@ -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.
"""
+2 -3
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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.
"""
+2 -3
View File
@@ -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
View File
@@ -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.
"""
+2 -2
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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."""
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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.
"""
+5 -3
View File
@@ -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
View File
@@ -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"]
+2 -2
View File
@@ -1,9 +1,9 @@
pre-commit # needed for pre-commit hooks in the git commit command
# For the test suite
pytest==7.4.0
pytest==7.4.3
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
pytest-xdist==3.5.0 # xdist runs tests in parallel
flaky # Used for flaky tests (flaky decorator)
beautifulsoup4 # used in test_official for parsing tg docs
+2 -2
View File
@@ -16,11 +16,11 @@ 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.
cachetools~=5.3.1 # callback-data!ext
cachetools~=5.3.2 # callback-data!ext
APScheduler~=3.10.4 # job-queue!ext
# pytz is required by APS and just needs the lower bound due to #2120
+1 -1
View File
@@ -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.2
+1 -1
View File
@@ -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
+274 -222
View File
@@ -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,14 +92,8 @@ 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.types import (
CorrectOptionID,
DVInput,
FileInput,
JSONDict,
ODVInput,
ReplyMarkup,
)
from telegram._utils.repr import build_repr_with_selected_attrs
from telegram._utils.types import CorrectOptionID, FileInput, JSONDict, ODVInput, ReplyMarkup
from telegram._utils.warnings import warn
from telegram._webhookinfo import WebhookInfo
from telegram.constants import InlineQueryLimit
@@ -111,18 +101,22 @@ from telegram.error import InvalidToken
from telegram.request import BaseRequest, RequestData
from telegram.request._httpxrequest import HTTPXRequest
from telegram.request._requestparameter import RequestParameter
from telegram.warnings import PTBUserWarning
from telegram.warnings import PTBDeprecationWarning, PTBUserWarning
if TYPE_CHECKING:
from telegram import (
InlineKeyboardMarkup,
InlineQueryResult,
InputFile,
InputMediaAudio,
InputMediaDocument,
InputMediaPhoto,
InputMediaVideo,
InputSticker,
LabeledPrice,
MessageEntity,
PassportElementError,
ShippingOption,
)
BT = TypeVar("BT", bound="Bot")
@@ -148,6 +142,8 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
finally:
await bot.shutdown()
.. seealso:: :meth:`__aenter__` and :meth:`__aexit__`.
Note:
* Most bot methods have the argument ``api_kwargs`` which allows passing arbitrary keywords
to the Telegram API. This can be used to access new features of the API before they are
@@ -290,8 +286,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 +304,76 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
self._freeze()
async def __aenter__(self: BT) -> BT:
"""
|async_context_manager| :meth:`initializes <initialize>` the Bot.
Returns:
The initialized Bot instance.
Raises:
:exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown`
is called in this case.
"""
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:
"""|async_context_manager| :meth:`shuts down <shutdown>` the Bot."""
# 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 +419,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 +515,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 +752,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,
@@ -765,7 +791,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
parse_mode: ODVInput[str] = DEFAULT_NONE,
entities: Optional[Sequence["MessageEntity"]] = None,
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
protect_content: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -900,7 +926,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
chat_id: Union[int, str],
from_chat_id: Union[str, int],
message_id: int,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: Optional[int] = None,
*,
@@ -966,7 +992,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
chat_id: Union[int, str],
photo: Union[FileInput, "PhotoSize"],
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -978,7 +1004,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1080,7 +1106,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
performer: Optional[str] = None,
title: Optional[str] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -1092,7 +1118,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1204,7 +1230,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
chat_id: Union[int, str],
document: Union[FileInput, "Document"],
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -1217,7 +1243,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1323,7 +1349,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
self,
chat_id: Union[int, str],
sticker: Union[FileInput, "Sticker"],
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1332,7 +1358,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
emoji: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1411,7 +1437,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
video: Union[FileInput, "Video"],
duration: Optional[int] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
width: Optional[int] = None,
@@ -1427,7 +1453,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1550,7 +1576,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
video_note: Union[FileInput, "VideoNote"],
duration: Optional[int] = None,
length: Optional[int] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1560,7 +1586,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1665,7 +1691,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
height: Optional[int] = None,
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1677,7 +1703,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1793,7 +1819,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
voice: Union[FileInput, "Voice"],
duration: Optional[int] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -1804,7 +1830,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1916,7 +1942,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
message_thread_id: Optional[int] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -2037,7 +2063,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
chat_id: Union[int, str],
latitude: Optional[float] = None,
longitude: Optional[float] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
live_period: Optional[int] = None,
@@ -2154,7 +2180,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 +2273,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,
@@ -2298,7 +2324,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
title: Optional[str] = None,
address: Optional[str] = None,
foursquare_id: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
foursquare_type: Optional[str] = None,
@@ -2422,7 +2448,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
phone_number: Optional[str] = None,
first_name: Optional[str] = None,
last_name: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
vcard: Optional[str] = None,
@@ -2521,11 +2547,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,
disable_notification: ODVInput[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 +2565,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 +2852,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 +2964,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 +3072,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 +3229,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 +3305,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 +3375,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,
@@ -3470,7 +3496,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
timeout: Optional[int] = None,
allowed_updates: Optional[Sequence[str]] = None,
*,
read_timeout: float = 2,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -3532,6 +3558,22 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
"allowed_updates": allowed_updates,
}
# The "or 0" is needed for the case where read_timeout is None.
if not isinstance(read_timeout, DefaultValue):
arg_read_timeout: float = read_timeout or 0
else:
try:
arg_read_timeout = self._request[0].read_timeout or 0
except NotImplementedError:
arg_read_timeout = 2
self._warn(
f"The class {self._request[0].__class__.__name__} does not override "
"the property `read_timeout`. Overriding this property will be mandatory in "
"future versions. Using 2 seconds as fallback.",
PTBDeprecationWarning,
stacklevel=3,
)
# Ideally we'd use an aggressive read timeout for the polling. However,
# * Short polling should return within 2 seconds.
# * Long polling poses a different problem: the connection might have been dropped while
@@ -3542,7 +3584,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
await self._post(
"getUpdates",
data,
read_timeout=read_timeout + timeout if timeout else read_timeout,
read_timeout=arg_read_timeout + timeout if timeout else arg_read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
@@ -3876,7 +3918,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 +4053,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 +4079,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 +4118,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 +4143,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.
@@ -4154,9 +4196,9 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
need_email: Optional[bool] = None,
need_shipping_address: Optional[bool] = None,
is_flexible: Optional[bool] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[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 +4359,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 +4418,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 +4525,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 +4599,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 +4612,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 +4636,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 +4651,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 +4659,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 +4670,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 +4705,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 +4783,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,
@@ -5103,7 +5163,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
photo: FileInput,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -5478,12 +5538,12 @@ 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],
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -5538,12 +5598,12 @@ 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,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -5636,16 +5696,16 @@ 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,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -5807,7 +5867,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 +6139,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 +6322,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,
@@ -6709,9 +6769,9 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Optional[Sequence["MessageEntity"]] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_markup: Optional[ReplyMarkup] = None,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: Optional[int] = None,
@@ -7843,14 +7903,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`"""
+6 -6
View File
@@ -26,7 +26,7 @@ from telegram._message import Message
from telegram._telegramobject import TelegramObject
from telegram._user import User
from telegram._utils.defaultvalue import DEFAULT_NONE
from telegram._utils.types import DVInput, JSONDict, ODVInput, ReplyMarkup
from telegram._utils.types import JSONDict, ODVInput, ReplyMarkup
if TYPE_CHECKING:
from telegram import (
@@ -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,
@@ -725,9 +725,9 @@ class CallbackQuery(TelegramObject):
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Optional[Sequence["MessageEntity"]] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_markup: Optional[ReplyMarkup] = None,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: Optional[int] = None,
+46 -44
View File
@@ -33,14 +33,7 @@ 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.defaultvalue import DEFAULT_NONE
from telegram._utils.types import (
CorrectOptionID,
DVInput,
FileInput,
JSONDict,
ODVInput,
ReplyMarkup,
)
from telegram._utils.types import CorrectOptionID, FileInput, JSONDict, ODVInput, ReplyMarkup
from telegram.helpers import escape_markdown
from telegram.helpers import mention_html as helpers_mention_html
from telegram.helpers import mention_markdown as helpers_mention_markdown
@@ -374,7 +367,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 +670,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 +700,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 +870,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 +902,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 +915,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 +937,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 +965,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,
@@ -1086,7 +1088,7 @@ class Chat(TelegramObject):
photo: FileInput,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1317,7 +1319,7 @@ class Chat(TelegramObject):
text: str,
parse_mode: ODVInput[str] = DEFAULT_NONE,
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1372,7 +1374,7 @@ class Chat(TelegramObject):
message_thread_id: Optional[int] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1448,7 +1450,7 @@ class Chat(TelegramObject):
self,
photo: Union[FileInput, "PhotoSize"],
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -1460,7 +1462,7 @@ class Chat(TelegramObject):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1501,7 +1503,7 @@ class Chat(TelegramObject):
phone_number: Optional[str] = None,
first_name: Optional[str] = None,
last_name: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
vcard: Optional[str] = None,
@@ -1553,7 +1555,7 @@ class Chat(TelegramObject):
performer: Optional[str] = None,
title: Optional[str] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -1565,7 +1567,7 @@ class Chat(TelegramObject):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1608,7 +1610,7 @@ class Chat(TelegramObject):
self,
document: Union[FileInput, "Document"],
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -1621,7 +1623,7 @@ class Chat(TelegramObject):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1703,7 +1705,7 @@ class Chat(TelegramObject):
async def send_game(
self,
game_short_name: str,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional["InlineKeyboardMarkup"] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1760,7 +1762,7 @@ class Chat(TelegramObject):
need_email: Optional[bool] = None,
need_shipping_address: Optional[bool] = None,
is_flexible: Optional[bool] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional["InlineKeyboardMarkup"] = None,
provider_data: Optional[Union[str, object]] = None,
@@ -1838,7 +1840,7 @@ class Chat(TelegramObject):
self,
latitude: Optional[float] = None,
longitude: Optional[float] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
live_period: Optional[int] = None,
@@ -1896,7 +1898,7 @@ class Chat(TelegramObject):
height: Optional[int] = None,
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1908,7 +1910,7 @@ class Chat(TelegramObject):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1951,7 +1953,7 @@ class Chat(TelegramObject):
async def send_sticker(
self,
sticker: Union[FileInput, "Sticker"],
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1960,7 +1962,7 @@ class Chat(TelegramObject):
emoji: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1999,7 +2001,7 @@ class Chat(TelegramObject):
title: Optional[str] = None,
address: Optional[str] = None,
foursquare_id: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
foursquare_type: Optional[str] = None,
@@ -2055,7 +2057,7 @@ class Chat(TelegramObject):
video: Union[FileInput, "Video"],
duration: Optional[int] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
width: Optional[int] = None,
@@ -2071,7 +2073,7 @@ class Chat(TelegramObject):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -2117,7 +2119,7 @@ class Chat(TelegramObject):
video_note: Union[FileInput, "VideoNote"],
duration: Optional[int] = None,
length: Optional[int] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2127,7 +2129,7 @@ class Chat(TelegramObject):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -2167,7 +2169,7 @@ class Chat(TelegramObject):
voice: Union[FileInput, "Voice"],
duration: Optional[int] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -2178,7 +2180,7 @@ class Chat(TelegramObject):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -2285,9 +2287,9 @@ class Chat(TelegramObject):
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Optional[Sequence["MessageEntity"]] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_markup: Optional[ReplyMarkup] = None,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: Optional[int] = None,
@@ -2335,9 +2337,9 @@ class Chat(TelegramObject):
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Optional[Sequence["MessageEntity"]] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_markup: Optional[ReplyMarkup] = None,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: Optional[int] = None,
@@ -2382,7 +2384,7 @@ class Chat(TelegramObject):
self,
from_chat_id: Union[str, int],
message_id: int,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: Optional[int] = None,
*,
@@ -2424,7 +2426,7 @@ class Chat(TelegramObject):
self,
chat_id: Union[int, str],
message_id: int,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: Optional[int] = None,
*,
+55 -16
View File
@@ -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__))
+1 -1
View File
@@ -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
View File
@@ -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):
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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,)
+1 -1
View File
@@ -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.
+2 -2
View File
@@ -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
+4 -1
View File
@@ -55,7 +55,10 @@ class MenuButton(TelegramObject):
__slots__ = ("type",)
def __init__(
self, type: str, *, api_kwargs: Optional[JSONDict] = None # skipcq: PYL-W0622
self,
type: str, # skipcq: PYL-W0622
*,
api_kwargs: Optional[JSONDict] = None,
): # pylint: disable=redefined-builtin
super().__init__(api_kwargs=api_kwargs)
self.type: str = type
+44 -42
View File
@@ -61,7 +61,6 @@ from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestam
from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue
from telegram._utils.types import (
CorrectOptionID,
DVInput,
FileInput,
JSONDict,
MarkdownVersion,
@@ -330,7 +329,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 +856,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`.
@@ -1058,7 +1060,7 @@ class Message(TelegramObject):
text: str,
parse_mode: ODVInput[str] = DEFAULT_NONE,
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1112,7 +1114,7 @@ class Message(TelegramObject):
self,
text: str,
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1176,7 +1178,7 @@ class Message(TelegramObject):
self,
text: str,
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1236,7 +1238,7 @@ class Message(TelegramObject):
self,
text: str,
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1305,7 +1307,7 @@ class Message(TelegramObject):
*,
quote: Optional[bool] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1354,7 +1356,7 @@ class Message(TelegramObject):
self,
photo: Union[FileInput, "PhotoSize"],
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -1367,7 +1369,7 @@ class Message(TelegramObject):
filename: Optional[str] = None,
quote: Optional[bool] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1416,7 +1418,7 @@ class Message(TelegramObject):
performer: Optional[str] = None,
title: Optional[str] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -1429,7 +1431,7 @@ class Message(TelegramObject):
filename: Optional[str] = None,
quote: Optional[bool] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1478,7 +1480,7 @@ class Message(TelegramObject):
self,
document: Union[FileInput, "Document"],
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -1492,7 +1494,7 @@ class Message(TelegramObject):
filename: Optional[str] = None,
quote: Optional[bool] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1543,7 +1545,7 @@ class Message(TelegramObject):
height: Optional[int] = None,
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1556,7 +1558,7 @@ class Message(TelegramObject):
filename: Optional[str] = None,
quote: Optional[bool] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1606,7 +1608,7 @@ class Message(TelegramObject):
async def reply_sticker(
self,
sticker: Union[FileInput, "Sticker"],
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1616,7 +1618,7 @@ class Message(TelegramObject):
*,
quote: Optional[bool] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1659,7 +1661,7 @@ class Message(TelegramObject):
video: Union[FileInput, "Video"],
duration: Optional[int] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
width: Optional[int] = None,
@@ -1676,7 +1678,7 @@ class Message(TelegramObject):
filename: Optional[str] = None,
quote: Optional[bool] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1728,7 +1730,7 @@ class Message(TelegramObject):
video_note: Union[FileInput, "VideoNote"],
duration: Optional[int] = None,
length: Optional[int] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1739,7 +1741,7 @@ class Message(TelegramObject):
filename: Optional[str] = None,
quote: Optional[bool] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1786,7 +1788,7 @@ class Message(TelegramObject):
voice: Union[FileInput, "Voice"],
duration: Optional[int] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -1798,7 +1800,7 @@ class Message(TelegramObject):
filename: Optional[str] = None,
quote: Optional[bool] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1845,7 +1847,7 @@ class Message(TelegramObject):
self,
latitude: Optional[float] = None,
longitude: Optional[float] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
live_period: Optional[int] = None,
@@ -1909,7 +1911,7 @@ class Message(TelegramObject):
title: Optional[str] = None,
address: Optional[str] = None,
foursquare_id: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
foursquare_type: Optional[str] = None,
@@ -1972,7 +1974,7 @@ class Message(TelegramObject):
phone_number: Optional[str] = None,
first_name: Optional[str] = None,
last_name: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
vcard: Optional[str] = None,
@@ -2181,7 +2183,7 @@ class Message(TelegramObject):
async def reply_game(
self,
game_short_name: str,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional["InlineKeyboardMarkup"] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2247,7 +2249,7 @@ class Message(TelegramObject):
need_email: Optional[bool] = None,
need_shipping_address: Optional[bool] = None,
is_flexible: Optional[bool] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional["InlineKeyboardMarkup"] = None,
provider_data: Optional[Union[str, object]] = None,
@@ -2333,7 +2335,7 @@ class Message(TelegramObject):
async def forward(
self,
chat_id: Union[int, str],
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: Optional[int] = None,
*,
@@ -2386,9 +2388,9 @@ class Message(TelegramObject):
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Optional[Sequence["MessageEntity"]] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_markup: Optional[ReplyMarkup] = None,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: Optional[int] = None,
@@ -2442,9 +2444,9 @@ class Message(TelegramObject):
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Optional[Sequence["MessageEntity"]] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_markup: Optional[ReplyMarkup] = None,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: Optional[int] = None,
@@ -2504,7 +2506,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 +2552,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 +2599,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 +2683,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 +2733,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 +2773,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 +2818,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 +2888,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,
+5 -1
View File
@@ -471,7 +471,11 @@ class _CredentialsBase(TelegramObject):
__slots__ = ("hash", "secret", "file_hash", "data_hash")
def __init__(
self, hash: str, secret: str, *, api_kwargs: Optional[JSONDict] = None # skipcq: PYL-W0622
self,
hash: str, # skipcq: PYL-W0622
secret: str,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
with self._unfrozen():
@@ -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)
+62 -9
View File
@@ -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):
"""
+29 -5
View File
@@ -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"
+2 -2
View File
@@ -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)
+2 -2
View File
@@ -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,
+1 -1
View File
@@ -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)
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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__()
+29 -36
View File
@@ -25,14 +25,7 @@ from telegram._inline.inlinekeyboardbutton import InlineKeyboardButton
from telegram._menubutton import MenuButton
from telegram._telegramobject import TelegramObject
from telegram._utils.defaultvalue import DEFAULT_NONE
from telegram._utils.types import (
CorrectOptionID,
DVInput,
FileInput,
JSONDict,
ODVInput,
ReplyMarkup,
)
from telegram._utils.types import CorrectOptionID, FileInput, JSONDict, ODVInput, ReplyMarkup
from telegram.helpers import mention_html as helpers_mention_html
from telegram.helpers import mention_markdown as helpers_mention_markdown
@@ -153,7 +146,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
@@ -391,7 +384,7 @@ class User(TelegramObject):
text: str,
parse_mode: ODVInput[str] = DEFAULT_NONE,
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -441,7 +434,7 @@ class User(TelegramObject):
self,
photo: Union[FileInput, "PhotoSize"],
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -453,7 +446,7 @@ class User(TelegramObject):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -504,7 +497,7 @@ class User(TelegramObject):
message_thread_id: Optional[int] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -551,7 +544,7 @@ class User(TelegramObject):
performer: Optional[str] = None,
title: Optional[str] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -563,7 +556,7 @@ class User(TelegramObject):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -648,7 +641,7 @@ class User(TelegramObject):
phone_number: Optional[str] = None,
first_name: Optional[str] = None,
last_name: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
vcard: Optional[str] = None,
@@ -745,7 +738,7 @@ class User(TelegramObject):
self,
document: Union[FileInput, "Document"],
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -758,7 +751,7 @@ class User(TelegramObject):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -801,7 +794,7 @@ class User(TelegramObject):
async def send_game(
self,
game_short_name: str,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional["InlineKeyboardMarkup"] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -861,7 +854,7 @@ class User(TelegramObject):
need_email: Optional[bool] = None,
need_shipping_address: Optional[bool] = None,
is_flexible: Optional[bool] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional["InlineKeyboardMarkup"] = None,
provider_data: Optional[Union[str, object]] = None,
@@ -942,7 +935,7 @@ class User(TelegramObject):
self,
latitude: Optional[float] = None,
longitude: Optional[float] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
live_period: Optional[int] = None,
@@ -1003,7 +996,7 @@ class User(TelegramObject):
height: Optional[int] = None,
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1015,7 +1008,7 @@ class User(TelegramObject):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1061,7 +1054,7 @@ class User(TelegramObject):
async def send_sticker(
self,
sticker: Union[FileInput, "Sticker"],
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1070,7 +1063,7 @@ class User(TelegramObject):
emoji: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1110,7 +1103,7 @@ class User(TelegramObject):
video: Union[FileInput, "Video"],
duration: Optional[int] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
width: Optional[int] = None,
@@ -1126,7 +1119,7 @@ class User(TelegramObject):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1177,7 +1170,7 @@ class User(TelegramObject):
title: Optional[str] = None,
address: Optional[str] = None,
foursquare_id: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
foursquare_type: Optional[str] = None,
@@ -1236,7 +1229,7 @@ class User(TelegramObject):
video_note: Union[FileInput, "VideoNote"],
duration: Optional[int] = None,
length: Optional[int] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1246,7 +1239,7 @@ class User(TelegramObject):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1289,7 +1282,7 @@ class User(TelegramObject):
voice: Union[FileInput, "Voice"],
duration: Optional[int] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -1300,7 +1293,7 @@ class User(TelegramObject):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1413,9 +1406,9 @@ class User(TelegramObject):
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Optional[Sequence["MessageEntity"]] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_markup: Optional[ReplyMarkup] = None,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: Optional[int] = None,
@@ -1466,9 +1459,9 @@ class User(TelegramObject):
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Optional[Sequence["MessageEntity"]] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_markup: Optional[ReplyMarkup] = None,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: Optional[int] = None,
+4 -5
View File
@@ -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]:
+8 -8
View File
@@ -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`"""
+45
View File
@@ -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}"
+6
View File
@@ -95,3 +95,9 @@ HTTPVersion = Literal["1.1", "2.0", "2"]
CorrectOptionID = Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
MarkdownVersion = Literal[1, 2]
SocketOpt = Union[
Tuple[int, int, int],
Tuple[int, int, Union[bytes, bytearray]],
Tuple[int, int, None, int],
]
+1 -1
View File
@@ -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=7, micro=0, releaselevel="final", serial=0
)
__version__: Final[str] = str(__version_info__)
+2 -2
View File
@@ -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",)
+40 -5
View File
@@ -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()
+1 -1
View File
@@ -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__`.
+103 -53
View File
@@ -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__)
@@ -150,6 +149,8 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
finally:
await application.shutdown()
.. seealso:: :meth:`__aenter__` and :meth:`__aexit__`.
Examples:
:any:`Echo Bot <examples.echobot>`
@@ -345,11 +346,44 @@ 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
"""|async_context_manager| :meth:`initializes <initialize>` the App.
Returns:
The initialized App instance.
Raises:
:exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown`
is called in this case.
"""
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:
"""|async_context_manager| :meth:`shuts down <shutdown>` the App."""
# 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 +434,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 +541,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 +570,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
@@ -676,7 +700,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
poll_interval: float = 0.0,
timeout: int = 10,
bootstrap_retries: int = -1,
read_timeout: float = 2,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -721,16 +745,37 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
* > 0 - retry up to X times
read_timeout (:obj:`float`, optional): Value to pass to
:paramref:`telegram.Bot.get_updates.read_timeout`. Defaults to ``2``.
:paramref:`telegram.Bot.get_updates.read_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
.. versionchanged:: 20.7
Defaults to :attr:`~telegram.request.BaseRequest.DEFAULT_NONE` instead of
``2``.
.. deprecated:: 20.7
Deprecated in favor of setting the timeout via
:meth:`telegram.ext.ApplicationBuilder.get_updates_read_timeout`.
write_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.Bot.get_updates.write_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
.. deprecated:: 20.7
Deprecated in favor of setting the timeout via
:meth:`telegram.ext.ApplicationBuilder.get_updates_write_timeout`.
connect_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.Bot.get_updates.connect_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
.. deprecated:: 20.7
Deprecated in favor of setting the timeout via
:meth:`telegram.ext.ApplicationBuilder.get_updates_connect_timeout`.
pool_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.Bot.get_updates.pool_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
.. deprecated:: 20.7
Deprecated in favor of setting the timeout via
:meth:`telegram.ext.ApplicationBuilder.get_updates_pool_timeout`.
drop_pending_updates (:obj:`bool`, optional): Whether to clean any pending updates on
Telegram servers before actually starting to poll. Default is :obj:`False`.
allowed_updates (List[:obj:`str`], optional): Passed to
@@ -759,6 +804,14 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
"Application.run_polling is only available if the application has an Updater."
)
if (read_timeout, write_timeout, connect_timeout, pool_timeout) != ((DEFAULT_NONE,) * 4):
warn(
"Setting timeouts via `Application.run_polling` is deprecated. "
"Please use `ApplicationBuilder.get_updates_*_timeout` instead.",
PTBDeprecationWarning,
stacklevel=2,
)
def error_callback(exc: TelegramError) -> None:
self.create_task(self.process_error(error=exc, update=None))
@@ -912,10 +965,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 +987,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,
)
@@ -1066,12 +1115,11 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
return await asyncio.create_task(coroutine)
# If user uses generator in python 3.12+, Exception will happen and we cannot do
# anything about it. (hence the type ignore if mypy is run on python 3.12-)
return await coroutine # type: ignore
return await coroutine # type: ignore[misc]
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 +1224,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 +1296,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(
@@ -1346,7 +1396,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
chat_id (:obj:`int`): The chat id to delete. The entry will be deleted even if it is
not empty.
"""
self._chat_data.pop(chat_id, None) # type: ignore[arg-type]
self._chat_data.pop(chat_id, None)
self._chat_ids_to_be_deleted_in_persistence.add(chat_id)
def drop_user_data(self, user_id: int) -> None:
@@ -1365,7 +1415,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
user_id (:obj:`int`): The user id to delete. The entry will be deleted even if it is
not empty.
"""
self._user_data.pop(user_id, None) # type: ignore[arg-type]
self._user_data.pop(user_id, None)
self._user_ids_to_be_deleted_in_persistence.add(user_id)
def migrate_chat_data(
+138 -31
View File
@@ -23,6 +23,7 @@ from typing import (
TYPE_CHECKING,
Any,
Callable,
Collection,
Coroutine,
Dict,
Generic,
@@ -32,9 +33,12 @@ from typing import (
Union,
)
import httpx
from telegram._bot import Bot
from telegram._utils.defaultvalue import DEFAULT_FALSE, DEFAULT_NONE, DefaultValue
from telegram._utils.types import DVInput, DVType, FilePathInput, HTTPVersion, ODVInput
from telegram._utils.types import DVInput, DVType, FilePathInput, HTTPVersion, ODVInput, SocketOpt
from telegram._utils.warnings import warn
from telegram.ext._application import Application
from telegram.ext._baseupdateprocessor import BaseUpdateProcessor, SimpleUpdateProcessor
from telegram.ext._contexttypes import ContextTypes
@@ -44,8 +48,10 @@ from telegram.ext._updater import Updater
from telegram.ext._utils.types import BD, BT, CCT, CD, JQ, UD
from telegram.request import BaseRequest
from telegram.request._httpxrequest import HTTPXRequest
from telegram.warnings import PTBDeprecationWarning
if TYPE_CHECKING:
from telegram import Update
from telegram.ext import BasePersistence, BaseRateLimiter, CallbackContext, Defaults
from telegram.ext._utils.types import RLARGS
@@ -66,14 +72,16 @@ _BOT_CHECKS = [
("request", "request instance"),
("get_updates_request", "get_updates_request instance"),
("connection_pool_size", "connection_pool_size"),
("proxy_url", "proxy_url"),
("proxy", "proxy"),
("socket_options", "socket_options"),
("pool_timeout", "pool_timeout"),
("connect_timeout", "connect_timeout"),
("read_timeout", "read_timeout"),
("write_timeout", "write_timeout"),
("http_version", "http_version"),
("get_updates_connection_pool_size", "get_updates_connection_pool_size"),
("get_updates_proxy_url", "get_updates_proxy_url"),
("get_updates_proxy", "get_updates_proxy"),
("get_updates_socket_options", "get_updates_socket_options"),
("get_updates_pool_timeout", "get_updates_pool_timeout"),
("get_updates_connect_timeout", "get_updates_connect_timeout"),
("get_updates_read_timeout", "get_updates_read_timeout"),
@@ -136,9 +144,10 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
"_get_updates_connect_timeout",
"_get_updates_connection_pool_size",
"_get_updates_pool_timeout",
"_get_updates_proxy_url",
"_get_updates_proxy",
"_get_updates_read_timeout",
"_get_updates_request",
"_get_updates_socket_options",
"_get_updates_write_timeout",
"_get_updates_http_version",
"_job_queue",
@@ -149,10 +158,11 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
"_post_stop",
"_private_key",
"_private_key_password",
"_proxy_url",
"_proxy",
"_rate_limiter",
"_read_timeout",
"_request",
"_socket_options",
"_token",
"_update_queue",
"_updater",
@@ -166,14 +176,16 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
self._base_url: DVType[str] = DefaultValue("https://api.telegram.org/bot")
self._base_file_url: DVType[str] = DefaultValue("https://api.telegram.org/file/bot")
self._connection_pool_size: DVInput[int] = DEFAULT_NONE
self._proxy_url: DVInput[str] = DEFAULT_NONE
self._proxy: DVInput[Union[str, httpx.Proxy, httpx.URL]] = DEFAULT_NONE
self._socket_options: DVInput[Collection[SocketOpt]] = DEFAULT_NONE
self._connect_timeout: ODVInput[float] = DEFAULT_NONE
self._read_timeout: ODVInput[float] = DEFAULT_NONE
self._write_timeout: ODVInput[float] = DEFAULT_NONE
self._pool_timeout: ODVInput[float] = DEFAULT_NONE
self._request: DVInput[BaseRequest] = DEFAULT_NONE
self._get_updates_connection_pool_size: DVInput[int] = DEFAULT_NONE
self._get_updates_proxy_url: DVInput[str] = DEFAULT_NONE
self._get_updates_proxy: DVInput[Union[str, httpx.Proxy, httpx.URL]] = DEFAULT_NONE
self._get_updates_socket_options: DVInput[Collection[SocketOpt]] = DEFAULT_NONE
self._get_updates_connect_timeout: ODVInput[float] = DEFAULT_NONE
self._get_updates_read_timeout: ODVInput[float] = DEFAULT_NONE
self._get_updates_write_timeout: ODVInput[float] = DEFAULT_NONE
@@ -186,7 +198,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
self._arbitrary_callback_data: Union[DefaultValue[bool], int] = DEFAULT_FALSE
self._local_mode: DVType[bool] = DEFAULT_FALSE
self._bot: DVInput[Bot] = DEFAULT_NONE
self._update_queue: DVType[Queue] = DefaultValue(Queue())
self._update_queue: DVType[Queue[Union[Update, object]]] = DefaultValue(Queue())
try:
self._job_queue: ODVInput[JobQueue] = DefaultValue(JobQueue())
@@ -214,7 +226,8 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
if not isinstance(getattr(self, f"{prefix}request"), DefaultValue):
return getattr(self, f"{prefix}request")
proxy_url = DefaultValue.get_value(getattr(self, f"{prefix}proxy_url"))
proxy = DefaultValue.get_value(getattr(self, f"{prefix}proxy"))
socket_options = DefaultValue.get_value(getattr(self, f"{prefix}socket_options"))
if get_updates:
connection_pool_size = (
DefaultValue.get_value(getattr(self, f"{prefix}connection_pool_size")) or 1
@@ -239,8 +252,9 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
return HTTPXRequest(
connection_pool_size=connection_pool_size,
proxy_url=proxy_url,
proxy=proxy,
http_version=http_version, # type: ignore[arg-type]
socket_options=socket_options,
**effective_timeouts,
)
@@ -258,7 +272,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
arbitrary_callback_data=DefaultValue.get_value(self._arbitrary_callback_data),
request=self._build_request(get_updates=False),
get_updates_request=self._build_request(get_updates=True),
rate_limiter=DefaultValue.get_value(self._rate_limiter),
rate_limiter=DefaultValue.get_value(self._rate_limiter), # type: ignore[arg-type]
local_mode=DefaultValue.get_value(self._local_mode),
)
@@ -303,7 +317,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
application: Application[
BT, CCT, UD, CD, BD, JQ
] = DefaultValue.get_value( # pylint: disable=not-callable
] = DefaultValue.get_value( # type: ignore[operator] # pylint: disable=not-callable
self._application_class
)(
bot=bot,
@@ -311,7 +325,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
updater=updater,
update_processor=self._update_processor,
job_queue=job_queue,
persistence=persistence,
persistence=persistence, # type: ignore[arg-type]
context_types=DefaultValue.get_value(self._context_types),
post_init=self._post_init,
post_shutdown=self._post_shutdown,
@@ -320,12 +334,12 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
)
if job_queue is not None:
job_queue.set_application(application) # type: ignore[arg-type]
job_queue.set_application(application) # type: ignore[arg-type, union-attr]
if persistence is not None:
# This raises an exception if persistence.store_data.callback_data is True
# but self.bot is not an instance of ExtBot - so no need to check that later on
persistence.set_bot(bot)
persistence.set_bot(bot) # type: ignore[union-attr]
return application
@@ -419,8 +433,11 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
if not isinstance(getattr(self, f"_{prefix}connection_pool_size"), DefaultValue):
raise RuntimeError(_TWO_ARGS_REQ.format(name, "connection_pool_size"))
if not isinstance(getattr(self, f"_{prefix}proxy_url"), DefaultValue):
raise RuntimeError(_TWO_ARGS_REQ.format(name, "proxy_url"))
if not isinstance(getattr(self, f"_{prefix}proxy"), DefaultValue):
raise RuntimeError(_TWO_ARGS_REQ.format(name, "proxy"))
if not isinstance(getattr(self, f"_{prefix}socket_options"), DefaultValue):
raise RuntimeError(_TWO_ARGS_REQ.format(name, "socket_options"))
if not isinstance(getattr(self, f"_{prefix}http_version"), DefaultValue):
raise RuntimeError(_TWO_ARGS_REQ.format(name, "http_version"))
@@ -486,21 +503,64 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
return self
def proxy_url(self: BuilderType, proxy_url: str) -> BuilderType:
"""Sets the proxy for the :paramref:`~telegram.request.HTTPXRequest.proxy_url`
parameter of :attr:`telegram.Bot.request`. Defaults to :obj:`None`.
"""Legacy name for :meth:`proxy`, kept for backward compatibility.
.. seealso:: :meth:`get_updates_proxy`
.. seealso:: :meth:`get_updates_proxy_url`
.. deprecated:: 20.7
Args:
proxy_url (:obj:`str`): The URL to the proxy server. See
:paramref:`telegram.request.HTTPXRequest.proxy_url` for more information.
proxy_url (:obj:`str` | ``httpx.Proxy`` | ``httpx.URL``): See
:paramref:`telegram.ext.ApplicationBuilder.proxy.proxy`.
Returns:
:class:`ApplicationBuilder`: The same builder with the updated argument.
"""
self._request_param_check(name="proxy_url", get_updates=False)
self._proxy_url = proxy_url
warn(
"`ApplicationBuilder.proxy_url` is deprecated since version "
"20.7. Use `ApplicationBuilder.proxy` instead.",
PTBDeprecationWarning,
stacklevel=2,
)
return self.proxy(proxy_url)
def proxy(self: BuilderType, proxy: Union[str, httpx.Proxy, httpx.URL]) -> BuilderType:
"""Sets the proxy for the :paramref:`~telegram.request.HTTPXRequest.proxy`
parameter of :attr:`telegram.Bot.request`. Defaults to :obj:`None`.
.. seealso:: :meth:`get_updates_proxy`
.. versionadded:: 20.7
Args:
proxy (:obj:`str` | ``httpx.Proxy`` | ``httpx.URL``): The URL to a proxy
server, a ``httpx.Proxy`` object or a ``httpx.URL`` object. See
:paramref:`telegram.request.HTTPXRequest.proxy` for more information.
Returns:
:class:`ApplicationBuilder`: The same builder with the updated argument.
"""
self._request_param_check(name="proxy", get_updates=False)
self._proxy = proxy
return self
def socket_options(self: BuilderType, socket_options: Collection[SocketOpt]) -> BuilderType:
"""Sets the options for the :paramref:`~telegram.request.HTTPXRequest.socket_options`
parameter of :attr:`telegram.Bot.request`. Defaults to :obj:`None`.
.. seealso:: :meth:`get_updates_socket_options`
.. versionadded:: 20.7
Args:
socket_options (Collection[:obj:`tuple`], optional): Socket options. See
:paramref:`telegram.request.HTTPXRequest.socket_options` for more information.
Returns:
:class:`ApplicationBuilder`: The same builder with the updated argument.
"""
self._request_param_check(name="socket_options", get_updates=False)
self._socket_options = socket_options
return self
def connect_timeout(self: BuilderType, connect_timeout: Optional[float]) -> BuilderType:
@@ -655,21 +715,68 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
return self
def get_updates_proxy_url(self: BuilderType, get_updates_proxy_url: str) -> BuilderType:
"""Sets the proxy for the :paramref:`telegram.request.HTTPXRequest.proxy_url`
parameter which is used for :meth:`telegram.Bot.get_updates`. Defaults to :obj:`None`.
"""Legacy name for :meth:`get_updates_proxy`, kept for backward compatibility.
.. seealso:: :meth:`proxy`
.. seealso:: :meth:`proxy_url`
.. deprecated:: 20.7
Args:
get_updates_proxy_url (:obj:`str`): The URL to the proxy server. See
:paramref:`telegram.request.HTTPXRequest.proxy_url` for more information.
get_updates_proxy_url (:obj:`str` | ``httpx.Proxy`` | ``httpx.URL``): See
:paramref:`telegram.ext.ApplicationBuilder.get_updates_proxy.get_updates_proxy`.
Returns:
:class:`ApplicationBuilder`: The same builder with the updated argument.
"""
self._request_param_check(name="proxy_url", get_updates=True)
self._get_updates_proxy_url = get_updates_proxy_url
warn(
"`ApplicationBuilder.get_updates_proxy_url` is deprecated since version "
"20.7. Use `ApplicationBuilder.get_updates_proxy` instead.",
PTBDeprecationWarning,
stacklevel=2,
)
return self.get_updates_proxy(get_updates_proxy_url)
def get_updates_proxy(
self: BuilderType, get_updates_proxy: Union[str, httpx.Proxy, httpx.URL]
) -> BuilderType:
"""Sets the proxy for the :paramref:`telegram.request.HTTPXRequest.proxy`
parameter which is used for :meth:`telegram.Bot.get_updates`. Defaults to :obj:`None`.
.. seealso:: :meth:`proxy`
.. versionadded:: 20.7
Args:
proxy (:obj:`str` | ``httpx.Proxy`` | ``httpx.URL``): The URL to a proxy server,
a ``httpx.Proxy`` object or a ``httpx.URL`` object. See
:paramref:`telegram.request.HTTPXRequest.proxy` for more information.
Returns:
:class:`ApplicationBuilder`: The same builder with the updated argument.
"""
self._request_param_check(name="proxy", get_updates=True)
self._get_updates_proxy = get_updates_proxy
return self
def get_updates_socket_options(
self: BuilderType, get_updates_socket_options: Collection[SocketOpt]
) -> BuilderType:
"""Sets the options for the :paramref:`~telegram.request.HTTPXRequest.socket_options`
parameter of :paramref:`telegram.Bot.get_updates_request`. Defaults to :obj:`None`.
.. seealso:: :meth:`socket_options`
.. versionadded:: 20.7
Args:
get_updates_socket_options (Collection[:obj:`tuple`], optional): Socket options. See
:paramref:`telegram.request.HTTPXRequest.socket_options` for more information.
Returns:
:class:`ApplicationBuilder`: The same builder with the updated argument.
"""
self._request_param_check(name="socket_options", get_updates=True)
self._get_updates_socket_options = get_updates_socket_options
return self
def get_updates_connect_timeout(
+16
View File
@@ -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,21 @@ 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`
"""
try:
callback_name = self.callback.__qualname__
except AttributeError:
callback_name = repr(self.callback)
return build_repr_with_selected_attrs(self, callback=callback_name)
@abstractmethod
def check_update(self, update: object) -> Optional[Union[bool, object]]:
"""
+45 -18
View File
@@ -27,6 +27,25 @@ class BaseUpdateProcessor(ABC):
"""An abstract base class for update processors. You can use this class to implement
your own update processor.
Instances of this class can be used as asyncio context managers, where
.. code:: python
async with processor:
# code
is roughly equivalent to
.. code:: python
try:
await processor.initialize()
# code
finally:
await processor.shutdown()
.. seealso:: :meth:`__aenter__` and :meth:`__aexit__`.
.. seealso:: :wiki:`Concurrency`
.. versionadded:: 20.4
@@ -48,6 +67,32 @@ 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":
"""|async_context_manager| :meth:`initializes <initialize>` the Processor.
Returns:
The initialized Processor instance.
Raises:
:exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown`
is called in this case.
"""
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:
"""|async_context_manager| :meth:`shuts down <shutdown>` 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 +150,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
+4 -2
View File
@@ -236,11 +236,13 @@ class CallbackContext(Generic[BT, UD, CD, BD]):
await self.application.persistence.refresh_bot_data(self.bot_data)
if self.application.persistence.store_data.chat_data and self._chat_id is not None:
await self.application.persistence.refresh_chat_data(
chat_id=self._chat_id, chat_data=self.chat_data # type: ignore[arg-type]
chat_id=self._chat_id,
chat_data=self.chat_data, # type: ignore[arg-type]
)
if self.application.persistence.store_data.user_data and self._user_id is not None:
await self.application.persistence.refresh_user_data(
user_id=self._user_id, user_data=self.user_data # type: ignore[arg-type]
user_id=self._user_id,
user_data=self.user_data, # type: ignore[arg-type]
)
def drop_callback_data(self, callback_query: CallbackQuery) -> None:
+9 -7
View File
@@ -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
+36 -4
View File
@@ -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
@@ -596,9 +621,16 @@ class ConversationHandler(BaseHandler[Update, CCT]):
self._conversations.update(current_conversations)
# above might be partly overridden but that's okay since we warn about that in
# add_handler
self._conversations.update_no_track(
await application.persistence.get_conversations(self.name)
)
stored_data = await application.persistence.get_conversations(self.name)
self._conversations.update_no_track(stored_data)
# Since CH.END is stored as normal state, we need to properly parse it here in order to
# actually end the conversation, i.e. delete the key from the _conversations dict
# This also makes sure that these entries are deleted from the persisted data on the next
# run of Application.update_persistence
for key, state in stored_data.items():
if state == self.END:
self._update_state(new_state=self.END, key=key)
out = {self.name: self._conversations}
+19 -22
View File
@@ -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
+82 -71
View File
@@ -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,14 +85,8 @@ 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.types import (
CorrectOptionID,
DVInput,
FileInput,
JSONDict,
ODVInput,
ReplyMarkup,
)
from telegram._utils.repr import build_repr_with_selected_attrs
from telegram._utils.types import CorrectOptionID, FileInput, JSONDict, ODVInput, ReplyMarkup
from telegram.ext._callbackdatacache import CallbackDataCache
from telegram.ext._utils.types import RLARGS
from telegram.request import BaseRequest
@@ -108,8 +99,11 @@ if TYPE_CHECKING:
InputMediaDocument,
InputMediaPhoto,
InputMediaVideo,
InputSticker,
LabeledPrice,
MessageEntity,
PassportElementError,
ShippingOption,
)
from telegram.ext import BaseRateLimiter, Defaults
@@ -246,6 +240,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
@@ -544,7 +549,7 @@ class ExtBot(Bot, Generic[RLARGS]):
timeout: Optional[int] = None,
allowed_updates: Optional[Sequence[str]] = None,
*,
read_timeout: float = 2,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -645,7 +650,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,
@@ -674,9 +679,9 @@ class ExtBot(Bot, Generic[RLARGS]):
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Optional[Sequence["MessageEntity"]] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_markup: Optional[ReplyMarkup] = None,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: Optional[int] = None,
@@ -733,12 +738,12 @@ 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,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -845,7 +850,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 +919,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,16 +1052,16 @@ 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,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -1329,7 +1334,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 +1367,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 +1404,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 +1460,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,
@@ -1506,7 +1511,7 @@ class ExtBot(Bot, Generic[RLARGS]):
chat_id: Union[int, str],
from_chat_id: Union[str, int],
message_id: int,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
protect_content: ODVInput[bool] = DEFAULT_NONE,
message_thread_id: Optional[int] = None,
*,
@@ -1554,7 +1559,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 +1660,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 +1786,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 +2037,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 +2050,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 +2076,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 +2111,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,
@@ -2156,7 +2167,7 @@ class ExtBot(Bot, Generic[RLARGS]):
height: Optional[int] = None,
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2168,7 +2179,7 @@ class ExtBot(Bot, Generic[RLARGS]):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -2207,7 +2218,7 @@ class ExtBot(Bot, Generic[RLARGS]):
performer: Optional[str] = None,
title: Optional[str] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -2219,7 +2230,7 @@ class ExtBot(Bot, Generic[RLARGS]):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -2279,7 +2290,7 @@ class ExtBot(Bot, Generic[RLARGS]):
phone_number: Optional[str] = None,
first_name: Optional[str] = None,
last_name: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
vcard: Optional[str] = None,
@@ -2354,7 +2365,7 @@ class ExtBot(Bot, Generic[RLARGS]):
chat_id: Union[int, str],
document: Union[FileInput, "Document"],
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -2367,7 +2378,7 @@ class ExtBot(Bot, Generic[RLARGS]):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -2397,11 +2408,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,
disable_notification: ODVInput[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,
@@ -2448,9 +2459,9 @@ class ExtBot(Bot, Generic[RLARGS]):
need_email: Optional[bool] = None,
need_shipping_address: Optional[bool] = None,
is_flexible: Optional[bool] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[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,
@@ -2508,7 +2519,7 @@ class ExtBot(Bot, Generic[RLARGS]):
chat_id: Union[int, str],
latitude: Optional[float] = None,
longitude: Optional[float] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
live_period: Optional[int] = None,
@@ -2562,7 +2573,7 @@ class ExtBot(Bot, Generic[RLARGS]):
message_thread_id: Optional[int] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -2596,7 +2607,7 @@ class ExtBot(Bot, Generic[RLARGS]):
parse_mode: ODVInput[str] = DEFAULT_NONE,
entities: Optional[Sequence["MessageEntity"]] = None,
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
protect_content: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2634,7 +2645,7 @@ class ExtBot(Bot, Generic[RLARGS]):
chat_id: Union[int, str],
photo: Union[FileInput, "PhotoSize"],
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -2646,7 +2657,7 @@ class ExtBot(Bot, Generic[RLARGS]):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -2733,7 +2744,7 @@ class ExtBot(Bot, Generic[RLARGS]):
self,
chat_id: Union[int, str],
sticker: Union[FileInput, "Sticker"],
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2742,7 +2753,7 @@ class ExtBot(Bot, Generic[RLARGS]):
emoji: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -2773,7 +2784,7 @@ class ExtBot(Bot, Generic[RLARGS]):
title: Optional[str] = None,
address: Optional[str] = None,
foursquare_id: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
foursquare_type: Optional[str] = None,
@@ -2821,7 +2832,7 @@ class ExtBot(Bot, Generic[RLARGS]):
video: Union[FileInput, "Video"],
duration: Optional[int] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
width: Optional[int] = None,
@@ -2837,7 +2848,7 @@ class ExtBot(Bot, Generic[RLARGS]):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -2875,7 +2886,7 @@ class ExtBot(Bot, Generic[RLARGS]):
video_note: Union[FileInput, "VideoNote"],
duration: Optional[int] = None,
length: Optional[int] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2885,7 +2896,7 @@ class ExtBot(Bot, Generic[RLARGS]):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -2917,7 +2928,7 @@ class ExtBot(Bot, Generic[RLARGS]):
voice: Union[FileInput, "Voice"],
duration: Optional[int] = None,
caption: Optional[str] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
reply_markup: Optional[ReplyMarkup] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
@@ -2928,7 +2939,7 @@ class ExtBot(Bot, Generic[RLARGS]):
*,
filename: Optional[str] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -2958,7 +2969,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,
@@ -3053,7 +3064,7 @@ class ExtBot(Bot, Generic[RLARGS]):
photo: FileInput,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
@@ -3115,9 +3126,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 +3204,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 +3249,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 +3307,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 +3331,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,12 +3460,12 @@ 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],
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = 20,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
+145 -73
View File
@@ -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
@@ -75,6 +76,17 @@ class JobQueue(Generic[CCT]):
Attributes:
scheduler (:class:`apscheduler.schedulers.asyncio.AsyncIOScheduler`): The scheduler.
Warning:
This scheduler is configured by :meth:`set_application`. Additional configuration
settings can be made by users. However, calling
:meth:`~apscheduler.schedulers.base.BaseScheduler.configure` will delete any
previous configuration settings. Therefore, please make sure to pass the values
returned by :attr:`scheduler_configuration` to the method call in addition to your
custom values.
Alternatively, you can also use methods like
:meth:`~apscheduler.schedulers.base.BaseScheduler.add_jobstore` to avoid using
:meth:`~apscheduler.schedulers.base.BaseScheduler.configure` altogether.
.. versionchanged:: 20.0
Uses :class:`~apscheduler.schedulers.asyncio.AsyncIOScheduler` instead of
:class:`~apscheduler.schedulers.background.BackgroundScheduler`
@@ -93,9 +105,65 @@ class JobQueue(Generic[CCT]):
self._application: Optional[weakref.ReferenceType[Application]] = None
self._executor = AsyncIOExecutor()
self.scheduler: AsyncIOScheduler = AsyncIOScheduler(
timezone=pytz.utc, executors={"default": self._executor}
)
self.scheduler: AsyncIOScheduler = AsyncIOScheduler(**self.scheduler_configuration)
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.")
@property
def scheduler_configuration(self) -> JSONDict:
"""Provides configuration values that are used by :class:`JobQueue` for :attr:`scheduler`.
Tip:
Since calling
:meth:`scheduler.configure() <apscheduler.schedulers.base.BaseScheduler.configure>`
deletes any previous setting, please make sure to pass these values to the method call
in addition to your custom values:
.. code-block:: python
scheduler.configure(..., **job_queue.scheduler_configuration)
Alternatively, you can also use methods like
:meth:`~apscheduler.schedulers.base.BaseScheduler.add_jobstore` to avoid using
:meth:`~apscheduler.schedulers.base.BaseScheduler.configure` altogether.
.. versionadded:: 20.7
Returns:
Dict[:obj:`str`, :obj:`object`]: The configuration values as dictionary.
"""
timezone: object = pytz.utc
if (
self._application
and isinstance(self.application.bot, ExtBot)
and self.application.bot.defaults
):
timezone = self.application.bot.defaults.tzinfo or pytz.utc
return {
"timezone": timezone,
"executors": {"default": self._executor},
}
def _tz_now(self) -> datetime.datetime:
return datetime.datetime.now(self.scheduler.timezone)
@@ -107,14 +175,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:
@@ -144,21 +212,7 @@ class JobQueue(Generic[CCT]):
"""
self._application = weakref.ref(application)
if isinstance(application.bot, ExtBot) and application.bot.defaults:
self.scheduler.configure(
timezone=application.bot.defaults.tzinfo or pytz.utc,
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.")
self.scheduler.configure(**self.scheduler_configuration)
@staticmethod
async def job_callback(job_queue: "JobQueue[CCT]", job: "Job[CCT]") -> None:
@@ -766,6 +820,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 +863,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 +915,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
+2 -2
View File
@@ -492,7 +492,7 @@ class PicklePersistence(BasePersistence[UD, CD, BD]):
"""
if self.chat_data is None:
return
self.chat_data.pop(chat_id, None) # type: ignore[arg-type]
self.chat_data.pop(chat_id, None)
if not self.on_flush:
if not self.single_file:
@@ -511,7 +511,7 @@ class PicklePersistence(BasePersistence[UD, CD, BD]):
"""
if self.user_data is None:
return
self.user_data.pop(user_id, None) # type: ignore[arg-type]
self.user_data.pop(user_id, None)
if not self.on_flush:
if not self.single_file:
+93 -33
View File
@@ -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
@@ -77,6 +78,8 @@ class Updater(AsyncContextManager["Updater"]):
finally:
await updater.shutdown()
.. seealso:: :meth:`__aenter__` and :meth:`__aexit__`.
.. seealso:: :wiki:`Architecture Overview <Architecture>`,
:wiki:`Builder Pattern <Builder-Pattern>`
@@ -124,6 +127,46 @@ 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
"""
|async_context_manager| :meth:`initializes <initialize>` the Updater.
Returns:
The initialized Updater instance.
Raises:
:exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown`
is called in this case.
"""
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:
"""|async_context_manager| :meth:`shuts down <shutdown>` the Updater."""
# 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,32 +206,12 @@ 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,
timeout: int = 10,
bootstrap_retries: int = -1,
read_timeout: float = 2,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -213,16 +236,40 @@ class Updater(AsyncContextManager["Updater"]):
* 0 - no retries
* > 0 - retry up to X times
read_timeout (:obj:`float`, optional): Value to pass to
:paramref:`telegram.Bot.get_updates.read_timeout`. Defaults to ``2``.
:paramref:`telegram.Bot.get_updates.read_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
.. versionchanged:: 20.7
Defaults to :attr:`~telegram.request.BaseRequest.DEFAULT_NONE` instead of
``2``.
.. deprecated:: 20.7
Deprecated in favor of setting the timeout via
:meth:`telegram.ext.ApplicationBuilder.get_updates_read_timeout` or
:paramref:`telegram.Bot.get_updates_request`.
write_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.Bot.get_updates.write_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
.. deprecated:: 20.7
Deprecated in favor of setting the timeout via
:meth:`telegram.ext.ApplicationBuilder.get_updates_write_timeout` or
:paramref:`telegram.Bot.get_updates_request`.
connect_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.Bot.get_updates.connect_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
.. deprecated:: 20.7
Deprecated in favor of setting the timeout via
:meth:`telegram.ext.ApplicationBuilder.get_updates_connect_timeout` or
:paramref:`telegram.Bot.get_updates_request`.
pool_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to
:paramref:`telegram.Bot.get_updates.pool_timeout`. Defaults to
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
.. deprecated:: 20.7
Deprecated in favor of setting the timeout via
:meth:`telegram.ext.ApplicationBuilder.get_updates_pool_timeout` or
:paramref:`telegram.Bot.get_updates_request`.
allowed_updates (List[:obj:`str`], optional): Passed to
:meth:`telegram.Bot.get_updates`.
drop_pending_updates (:obj:`bool`, optional): Whether to clean any pending updates on
@@ -248,6 +295,10 @@ class Updater(AsyncContextManager["Updater"]):
:exc:`RuntimeError`: If the updater is already running or was not initialized.
"""
# We refrain from issuing deprecation warnings for the timeout parameters here, as we
# already issue them in `Application`. This means that there are no warnings when using
# `Updater` without `Application`, but this is a rather special use case.
if error_callback and asyncio.iscoroutinefunction(error_callback):
raise TypeError(
"The `error_callback` must not be a coroutine function! Use an ordinary function "
@@ -293,7 +344,7 @@ class Updater(AsyncContextManager["Updater"]):
self,
poll_interval: float,
timeout: int,
read_timeout: float,
read_timeout: ODVInput[float],
write_timeout: ODVInput[float],
connect_timeout: ODVInput[float],
pool_timeout: ODVInput[float],
@@ -378,16 +429,25 @@ class Updater(AsyncContextManager["Updater"]):
_LOGGER.debug(
"Calling `get_updates` one more time to mark all fetched updates as read."
)
await self.bot.get_updates(
offset=self._last_update_id,
# We don't want to do long polling here!
timeout=0,
read_timeout=read_timeout,
connect_timeout=connect_timeout,
write_timeout=write_timeout,
pool_timeout=pool_timeout,
allowed_updates=allowed_updates,
)
try:
await self.bot.get_updates(
offset=self._last_update_id,
# We don't want to do long polling here!
timeout=0,
read_timeout=read_timeout,
connect_timeout=connect_timeout,
write_timeout=write_timeout,
pool_timeout=pool_timeout,
allowed_updates=allowed_updates,
)
except TelegramError as exc:
_LOGGER.error(
"Error while calling `get_updates` one more time to mark all fetched updates "
"as read: %s. Suppressing error to ensure graceful shutdown. When polling for "
"updates is restarted, updates may be fetched again. Please adjust timeouts "
"via `ApplicationBuilder` or the parameter `get_updates_request` of `Bot`.",
exc_info=exc,
)
self.__polling_cleanup_cb = _get_updates_cleanup
+11 -9
View File
@@ -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():
@@ -99,7 +99,9 @@ class TrackingDict(UserDict, Generic[_KT, _VT]):
# Mypy seems a bit inconsistent about what it wants as types for `default` and return value
# so we just ignore a bit
def pop( # type: ignore[override]
self, key: _KT, default: _VT = DEFAULT_NONE # type: ignore[assignment]
self,
key: _KT,
default: _VT = DEFAULT_NONE, # type: ignore[assignment]
) -> _VT:
if key in self:
self.__track_write(key)
+103 -35
View File
@@ -66,6 +66,7 @@ __all__ = (
"LOCATION",
"Language",
"MessageFilter",
"Mention",
"PASSPORT_DATA",
"PHOTO",
"POLL",
@@ -91,7 +92,6 @@ __all__ = (
"VOICE",
"ViaBot",
)
import mimetypes
import re
from abc import ABC, abstractmethod
@@ -99,6 +99,7 @@ from typing import (
Collection,
Dict,
FrozenSet,
Iterable,
List,
Match,
NoReturn,
@@ -182,6 +183,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 +239,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 +764,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
@@ -1521,6 +1522,73 @@ LOCATION = _Location(name="filters.LOCATION")
"""Messages that contain :attr:`telegram.Message.location`."""
class Mention(MessageFilter):
"""Messages containing mentions of specified users or chats.
Examples:
.. code-block:: python
MessageHandler(filters.Mention("username"), callback)
MessageHandler(filters.Mention(["@username", 123456]), callback)
.. versionadded:: 20.7
Args:
mentions (:obj:`int` | :obj:`str` | :class:`telegram.User` | Collection[:obj:`int` | \
:obj:`str` | :class:`telegram.User`]):
Specifies the users and chats to filter for. Messages that do not mention at least one
of the specified users or chats will not be handled. Leading ``'@'`` s in usernames
will be discarded.
"""
__slots__ = ("_mentions",)
def __init__(self, mentions: SCT[Union[int, str, TGUser]]):
super().__init__(name=f"filters.Mention({mentions})")
if isinstance(mentions, Iterable) and not isinstance(mentions, str):
self._mentions = {self._fix_mention_username(mention) for mention in mentions}
else:
self._mentions = {self._fix_mention_username(mentions)}
@staticmethod
def _fix_mention_username(mention: Union[int, str, TGUser]) -> Union[int, str, TGUser]:
if not isinstance(mention, str):
return mention
return mention.lstrip("@")
@classmethod
def _check_mention(cls, message: Message, mention: Union[int, str, TGUser]) -> bool:
if not message.entities:
return False
entity_texts = message.parse_entities(
types=[MessageEntity.MENTION, MessageEntity.TEXT_MENTION]
)
if isinstance(mention, TGUser):
return any(
mention.id == entity.user.id
or mention.username == entity.user.username
or mention.username == cls._fix_mention_username(entity_texts[entity])
for entity in message.entities
if entity.user
) or any(
mention.username == cls._fix_mention_username(entity_text)
for entity_text in entity_texts.values()
)
if isinstance(mention, int):
return bool(
any(mention == entity.user.id for entity in message.entities if entity.user)
)
return any(
mention == cls._fix_mention_username(entity_text)
for entity_text in entity_texts.values()
)
def filter(self, message: Message) -> bool:
return any(self._check_mention(message, mention) for mention in self._mentions)
class _PassportData(MessageFilter):
__slots__ = ()
+56 -2
View File
@@ -27,6 +27,7 @@ from telegram._utils.defaultvalue import DEFAULT_NONE as _DEFAULT_NONE
from telegram._utils.defaultvalue import DefaultValue
from telegram._utils.logging import get_logger
from telegram._utils.types import JSONDict, ODVInput
from telegram._utils.warnings import warn
from telegram._version import __version__ as ptb_ver
from telegram.error import (
BadRequest,
@@ -39,6 +40,7 @@ from telegram.error import (
TelegramError,
)
from telegram.request._requestdata import RequestData
from telegram.warnings import PTBDeprecationWarning
RT = TypeVar("RT", bound="BaseRequest")
@@ -70,6 +72,8 @@ class BaseRequest(
finally:
await request_object.shutdown()
.. seealso:: :meth:`__aenter__` and :meth:`__aexit__`.
Tip:
JSON encoding and decoding is done with the standard library's :mod:`json` by default.
To use a custom library for this, you can override :meth:`parse_json_payload` and implement
@@ -99,6 +103,15 @@ class BaseRequest(
"""
async def __aenter__(self: RT) -> RT:
"""|async_context_manager| :meth:`initializes <initialize>` the Request.
Returns:
The initialized Request instance.
Raises:
:exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown`
is called in this case.
"""
try:
await self.initialize()
return self
@@ -112,10 +125,29 @@ class BaseRequest(
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType],
) -> None:
"""|async_context_manager| :meth:`shuts down <shutdown>` the Request."""
# 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 read_timeout(self) -> Optional[float]:
"""This property must return the default read timeout in seconds used by this class.
More precisely, the returned value should be the one used when
:paramref:`post.read_timeout` of :meth:post` is not passed/equal to :attr:`DEFAULT_NONE`.
.. versionadded:: 20.7
Warning:
For now this property does not need to be implemented by subclasses and will raise
:exc:`NotImplementedError` if accessed without being overridden. However, in future
versions, this property will be abstract and must be implemented by subclasses.
Returns:
:obj:`float` | :obj:`None`: The read timeout in seconds.
"""
raise NotImplementedError
@abc.abstractmethod
async def initialize(self) -> None:
"""Initialize resources used by this class. Must be implemented by a subclass."""
@@ -271,8 +303,28 @@ class BaseRequest(
TelegramError
"""
# TGs response also has the fields 'ok' and 'error_code'.
# However, we rather rely on the HTTP status code for now.
# Import needs to be here since HTTPXRequest is a subclass of BaseRequest
from telegram.request import HTTPXRequest # pylint: disable=import-outside-toplevel
# 20 is the documented default value for all the media related bot methods and custom
# implementations of BaseRequest may explicitly rely on that. Hence, we follow the
# standard deprecation policy and deprecate starting with version 20.7.
# For our own implementation HTTPXRequest, we can handle that ourselves, so we skip the
# warning in that case.
has_files = request_data and request_data.multipart_data
if (
has_files
and not isinstance(self, HTTPXRequest)
and isinstance(write_timeout, DefaultValue)
):
warn(
f"The `write_timeout` parameter passed to {self.__class__.__name__}.do_request "
"will default to `BaseRequest.DEFAULT_NONE` instead of 20 in future versions "
"for *all* methods of the `Bot` class, including methods sending media.",
PTBDeprecationWarning,
stacklevel=3,
)
write_timeout = 20
try:
code, payload = await self.do_request(
@@ -301,6 +353,8 @@ class BaseRequest(
# In some special cases, we can raise more informative exceptions:
# see https://core.telegram.org/bots/api#responseparameters and
# https://core.telegram.org/bots/api#making-requests
# TGs response also has the fields 'ok' and 'error_code'.
# However, we rather rely on the HTTP status code for now.
parameters = response_data.get("parameters")
if parameters:
migrate_to_chat_id = parameters.get("migrate_to_chat_id")
+82 -33
View File
@@ -17,16 +17,18 @@
# 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 methods to make POST and GET requests using the httpx library."""
from typing import Optional, Tuple
from typing import Collection, Optional, Tuple, Union
import httpx
from telegram._utils.defaultvalue import DefaultValue
from telegram._utils.logging import get_logger
from telegram._utils.types import HTTPVersion, ODVInput
from telegram._utils.types import HTTPVersion, ODVInput, SocketOpt
from telegram._utils.warnings import warn
from telegram.error import NetworkError, TimedOut
from telegram.request._baserequest import BaseRequest
from telegram.request._requestdata import RequestData
from telegram.warnings import PTBDeprecationWarning
# Note to future devs:
# Proxies are currently only tested manually. The httpx development docs have a nice guide on that:
@@ -49,17 +51,10 @@ class HTTPXRequest(BaseRequest):
Note:
Independent of the value, one additional connection will be reserved for
:meth:`telegram.Bot.get_updates`.
proxy_url (:obj:`str`, optional): The URL to the proxy server. For example
``'http://127.0.0.1:3128'`` or ``'socks5://127.0.0.1:3128'``. Defaults to :obj:`None`.
proxy_url (:obj:`str`, optional): Legacy name for :paramref:`proxy`, kept for backward
compatibility. Defaults to :obj:`None`.
Note:
* The proxy URL can also be set via the environment variables ``HTTPS_PROXY`` or
``ALL_PROXY``. See `the docs of httpx`_ for more info.
* For Socks5 support, additional dependencies are required. Make sure to install
PTB via :command:`pip install "python-telegram-bot[socks]"` in this case.
* Socks5 proxies can not be set via environment variables.
.. _the docs of httpx: https://www.python-httpx.org/environment_variables/#proxies
.. deprecated:: 20.7
read_timeout (:obj:`float` | :obj:`None`, optional): If passed, specifies the maximum
amount of time (in seconds) to wait for a response from Telegram's server.
This value is used unless a different value is passed to :meth:`do_request`.
@@ -91,6 +86,32 @@ class HTTPXRequest(BaseRequest):
.. versionchanged:: 20.5
Accept ``"2"`` as a valid value.
socket_options (Collection[:obj:`tuple`], optional): Socket options to be passed to the
underlying `library \
<https://www.encode.io/httpcore/async/#httpcore.AsyncConnectionPool.__init__>`_.
Note:
The values accepted by this parameter depend on the operating system.
This is a low-level parameter and should only be used if you are familiar with
these concepts.
.. versionadded:: 20.7
proxy (:obj:`str` | ``httpx.Proxy`` | ``httpx.URL``, optional): The URL to a proxy server,
a ``httpx.Proxy`` object or a ``httpx.URL`` object. For example
``'http://127.0.0.1:3128'`` or ``'socks5://127.0.0.1:3128'``. Defaults to :obj:`None`.
Note:
* The proxy URL can also be set via the environment variables ``HTTPS_PROXY`` or
``ALL_PROXY``. See `the docs of httpx`_ for more info.
* HTTPS proxies can be configured by passing a ``httpx.Proxy`` object with
a corresponding ``ssl_context``.
* For Socks5 support, additional dependencies are required. Make sure to install
PTB via :command:`pip install "python-telegram-bot[socks]"` in this case.
* Socks5 proxies can not be set via environment variables.
.. _the docs of httpx: https://www.python-httpx.org/environment_variables/#proxies
.. versionadded:: 20.7
"""
@@ -99,13 +120,27 @@ class HTTPXRequest(BaseRequest):
def __init__(
self,
connection_pool_size: int = 1,
proxy_url: Optional[str] = None,
proxy_url: Optional[Union[str, httpx.Proxy, httpx.URL]] = None,
read_timeout: Optional[float] = 5.0,
write_timeout: Optional[float] = 5.0,
connect_timeout: Optional[float] = 5.0,
pool_timeout: Optional[float] = 1.0,
http_version: HTTPVersion = "1.1",
socket_options: Optional[Collection[SocketOpt]] = None,
proxy: Optional[Union[str, httpx.Proxy, httpx.URL]] = None,
):
if proxy_url is not None and proxy is not None:
raise ValueError("The parameters `proxy_url` and `proxy` are mutually exclusive.")
if proxy_url is not None:
proxy = proxy_url
warn(
"The parameter `proxy_url` is deprecated since version 20.7. Use `proxy` "
"instead.",
PTBDeprecationWarning,
stacklevel=2,
)
self._http_version = http_version
timeout = httpx.Timeout(
connect=connect_timeout,
@@ -122,16 +157,21 @@ class HTTPXRequest(BaseRequest):
raise ValueError("`http_version` must be either '1.1', '2.0' or '2'.")
http1 = http_version == "1.1"
# See https://github.com/python-telegram-bot/python-telegram-bot/pull/3542
# for why we need to use `dict()` here.
self._client_kwargs = dict( # pylint: disable=use-dict-literal # noqa: C408
timeout=timeout,
proxies=proxy_url,
limits=limits,
http1=http1,
http2=not http1,
http_kwargs = {"http1": http1, "http2": not http1}
transport = (
httpx.AsyncHTTPTransport(
socket_options=socket_options,
)
if socket_options
else None
)
self._client_kwargs = {
"timeout": timeout,
"proxies": proxy,
"limits": limits,
"transport": transport,
**http_kwargs,
}
try:
self._client = self._build_client()
@@ -158,6 +198,16 @@ class HTTPXRequest(BaseRequest):
"""
return self._http_version
@property
def read_timeout(self) -> Optional[float]:
"""See :attr:`BaseRequest.read_timeout`.
Returns:
:obj:`float` | :obj:`None`: The default read timeout in seconds as passed to
:paramref:`HTTPXRequest.read_timeout`.
"""
return self._client.timeout.read
def _build_client(self) -> httpx.AsyncClient:
return httpx.AsyncClient(**self._client_kwargs) # type: ignore[arg-type]
@@ -188,17 +238,25 @@ class HTTPXRequest(BaseRequest):
if self._client.is_closed:
raise RuntimeError("This HTTPXRequest is not initialized!")
files = request_data.multipart_data if request_data else None
data = request_data.json_parameters if request_data else None
# If user did not specify timeouts (for e.g. in a bot method), use the default ones when we
# created this instance.
if isinstance(read_timeout, DefaultValue):
read_timeout = self._client.timeout.read
if isinstance(write_timeout, DefaultValue):
write_timeout = self._client.timeout.write
if isinstance(connect_timeout, DefaultValue):
connect_timeout = self._client.timeout.connect
if isinstance(pool_timeout, DefaultValue):
pool_timeout = self._client.timeout.pool
if isinstance(write_timeout, DefaultValue):
# Making the networking backend decide on the proper timeout values instead of doing
# it via the default values of the Bot methods was introduced in version 20.7.
# We hard-code the value here for now until we add additional parameters to this
# class to control the media_write_timeout separately.
write_timeout = self._client.timeout.write if not files else 20
timeout = httpx.Timeout(
connect=connect_timeout,
read=read_timeout,
@@ -206,15 +264,6 @@ class HTTPXRequest(BaseRequest):
pool=pool_timeout,
)
# TODO p0: On Linux, use setsockopt to properly set socket level keepalive.
# (socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 120)
# (socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 30)
# (socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 8)
# TODO p4: Support setsockopt on lesser platforms than Linux.
files = request_data.multipart_data if request_data else None
data = request_data.json_parameters if request_data else None
try:
res = await self._client.request(
method=method,
+1 -2
View File
@@ -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
View File
@@ -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:
+1 -1
View File
@@ -322,7 +322,7 @@ class TestAnimationWithRequest(TestAnimationBase):
)
assert message.reply_to_message is None
else:
with pytest.raises(BadRequest, match="message not found"):
with pytest.raises(BadRequest, match="Message to reply not found"):
await default_bot.send_animation(
chat_id, animation, reply_to_message_id=reply_to_message.message_id
)

Some files were not shown because too many files have changed in this diff Show More