Compare commits

...

46 Commits

Author SHA1 Message Date
Bibo-Joshi 2ac52018c2 Bump Version to v21.9 (#4601) 2024-12-07 13:39:41 +01:00
dependabot[bot] ca7a30963d Update aiolimiter requirement from ~=1.1.0 to >=1.1,<1.3 (#4595)
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>
2024-12-07 11:31:52 +01:00
dependabot[bot] c42a230b96 Bump pytest from 8.3.3 to 8.3.4 (#4596)
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>
2024-12-07 11:15:53 +01:00
Luis Pérez ce9742a602 Use MessageLimit.DEEP_LINK_LENGTH in helpers.create_deep_linked_url (#4597) 2024-12-07 10:25:13 +01:00
Bibo-Joshi 43279543a3 Full Support for Bot API 8.1 (#4594) 2024-12-07 10:20:08 +01:00
Luis Pérez eda2172617 Allow Sequence Input for allowed_updates in Application and Updater Methods (#4589) 2024-12-04 21:20:12 +01:00
dependabot[bot] 89dfa37dbf Bump codecov/codecov-action from 4 to 5 (#4585)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-04 21:04:47 +01:00
Luis Pérez 3709c2fa93 Bump pylint to v3.3.2 to Improve Python 3.13 Support (#4590) 2024-12-04 20:54:03 +01:00
dependabot[bot] da93fe94ae Bump srvaroa/labeler from 1.11.1 to 1.12.0 (#4586)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-02 06:17:52 +01:00
Bibo-Joshi cec34e4bca Bump Version to v21.8 (#4583) 2024-12-01 12:19:55 +01:00
Bibo-Joshi ffe23be992 Documentation Improvements (#4573, #4565)
Co-authored-by: Snehashish Biswas <coderrx06@gmail.com>
Co-authored-by: poolitzer <github@poolitzer.eu>
2024-12-01 11:47:38 +01:00
Bibo-Joshi ef1685c436 Full Support for Bot API 8.0 (#4566, #4568, #4570, #4571, #4574, #4576, #4572) 2024-12-01 10:26:48 +01:00
Bibo-Joshi 151123745e Bump Version to v21.7 (#4557) 2024-11-04 21:29:59 +01:00
Bibo-Joshi 0eb11ff3e9 Documentation Improvements (#4536, #4556)
Co-authored-by: Abubakar Alaya <ecode5814@gmail.com>
2024-11-04 20:38:41 +01:00
Bibo-Joshi dab75fb963 Add Message.reply_paid_media (#4551) 2024-11-04 20:33:56 +01:00
Bibo-Joshi 62f89758d7 Bot API 7.11 (#4546) 2024-11-04 20:11:10 +01:00
Bibo-Joshi 7a8f4412b2 Update Issue Templates to Use Issue Types (#4553) 2024-11-04 19:08:04 +01:00
Bibo-Joshi 032a859149 Update Automation to Label Changes (#4552) 2024-11-03 21:43:40 +01:00
Bibo-Joshi 507d6bc0e3 Improve Exception Handling in File.download_* (#4542) 2024-11-03 16:35:16 +01:00
dependabot[bot] bd6a60bb30 Bump srvaroa/labeler from 1.11.0 to 1.11.1 (#4549)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-02 18:04:50 +01:00
Bibo-Joshi 3c8f6ed42b Fix Linkcheck Workflow (#4545) 2024-11-01 22:17:14 +01:00
Bibo-Joshi 6540f288f5 Use sphinx-build-compatibility to Keep Sphinx Compatibility (#4492) 2024-10-31 08:27:58 +01:00
Pablo Martínez 5ab82a9c2b Drop Support for Python 3.8 (#4398)
Co-authored-by: poolitzer <github@poolitzer.eu>
Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
2024-10-24 20:48:49 +02:00
Bibo-Joshi 847b97f86e Use Stable Python 3.13 Release in Test Suite (#4535) 2024-10-24 20:03:16 +02:00
Siloé Garcez efacc3dd1b Allow Sequence in Application.add_handlers (#4531) 2024-10-23 22:14:03 +02:00
dependabot[bot] 2ce687c8f1 Bump sphinx from 8.0.2 to 8.1.3 (#4532)
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>
2024-10-21 19:40:12 +02:00
dependabot[bot] a39a59ee9b Bump sphinxcontrib-mermaid from 0.9.2 to 1.0.0 (#4529)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-21 19:12:44 +02:00
dependabot[bot] 06854633ab Bump srvaroa/labeler from 1.10.1 to 1.11.0 (#4509)
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>
2024-10-02 03:00:45 +02:00
dependabot[bot] 9a8b208ef7 Bump Bibo-Joshi/pyright-type-completeness from 1.0.0 to 1.0.1 (#4510)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-02 02:57:34 +02:00
Bibo-Joshi 2f06902518 Improve Test Instability Caused by Message Fixtures (#4507) 2024-10-01 07:16:29 +02:00
Bibo-Joshi 2bc65560eb Stabilize Some Flaky Tests (#4500) 2024-09-25 18:53:26 +02:00
Bibo-Joshi 79e589b39e Reduce Creation of HTTP Clients in Tests (#4493) 2024-09-25 17:17:55 +02:00
Bibo-Joshi bd3cdbcdbd Update pytest-xdist Usage (#4491) 2024-09-22 19:49:48 +02:00
Bibo-Joshi 9709c03b35 Fix Failing Tests by Making Them Independent (#4494) 2024-09-21 18:49:33 +02:00
Bibo-Joshi 3409f51107 Introduce Codecov's Test Analysis (#4487) 2024-09-21 17:15:22 +02:00
Bibo-Joshi 2eae2830f3 Maintenance Work on Bot Tests (#4489)
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
2024-09-21 11:34:25 +02:00
Bibo-Joshi 28d19c3b9a Introduce conftest.py for File Related Tests (#4488)
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
2024-09-21 11:33:52 +02:00
Bibo-Joshi e314e78d06 Bump Version to v21.6 (#4486) 2024-09-19 20:17:08 +02:00
Harshil 67a97ae5a7 API 7.10 (#4461, #4460, #4463, #4464)
Co-authored-by: aelkheir <90580077+aelkheir@users.noreply.github.com>
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
2024-09-17 18:09:19 +02:00
dependabot[bot] 9248c539d0 Bump pytest from 8.3.2 to 8.3.3 (#4475)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-14 08:17:31 +02:00
Bibo-Joshi 6b5e46cc08 Improve Type Completeness (#4466) 2024-09-14 08:16:39 +02:00
Harshil b3155b2e55 Update Python 3.13 Test Suite to RC2 (#4471) 2024-09-13 19:32:22 +02:00
Bibo-Joshi ec909e62cf Enforce the offline_bot Fixture in Test*WithoutRequest (#4465) 2024-09-13 19:10:09 +02:00
Bibo-Joshi 1223e851c3 Add Parameter httpx_kwargs to HTTPXRequest (#4451) 2024-09-11 22:34:18 +02:00
Bibo-Joshi 0b352b043e Make Tests for telegram.ext Independent of Networking (#4454) 2024-09-09 07:32:32 +02:00
Bibo-Joshi b9d2efdec5 Rename Testing Base Classes (#4453) 2024-09-03 05:24:25 +02:00
330 changed files with 7187 additions and 3690 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
name: Bug Report
description: Create a report to help us improve
title: "[BUG]"
labels: ["bug :bug:"]
labels: ["📋 triage"]
type: '🐛 bug'
body:
- type: markdown
+2 -2
View File
@@ -1,7 +1,7 @@
name: Feature Request
description: Suggest an idea for this project
title: "[FEATURE]"
labels: ["enhancement"]
labels: ["📋 triage"]
type: '💡 feature'
body:
- type: textarea
+1 -1
View File
@@ -1,7 +1,7 @@
name: Question
description: Get help with errors or general questions
title: "[QUESTION]"
labels: ["question"]
type: '❔ question'
body:
- type: markdown
+2 -2
View File
@@ -3,7 +3,7 @@
version: 1
labels:
- label: "dependencies"
- label: "⚙️ dependencies"
authors: ["dependabot[bot]", "pre-commit-ci[bot]"]
- label: "code quality"
- label: "🛠 code-quality"
authors: ["pre-commit-ci[bot]"]
+4 -1
View File
@@ -3,6 +3,9 @@ on:
schedule:
# First day of month at 05:46 in every 2nd month
- cron: '46 5 1 */2 *'
pull_request:
paths:
- .github/workflows/docs-linkcheck.yml
jobs:
test-sphinx-build:
@@ -10,7 +13,7 @@ jobs:
runs-on: ${{matrix.os}}
strategy:
matrix:
python-version: [3.10]
python-version: ['3.10']
os: [ubuntu-latest]
fail-fast: False
steps:
+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.10.1
- uses: srvaroa/labeler@v1.12.0
# Config file at .github/labeler.yml
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
days-before-stale: 3
days-before-close: 2
days-before-pr-stale: -1
stale-issue-label: 'stale'
stale-issue-label: '📋 stale'
only-labels: 'question'
stale-issue-message: ''
close-issue-message: 'This issue has been automatically closed due to inactivity. Feel free to comment in order to reopen or ask again in our Telegram support group at https://t.me/pythontelegrambotgroup.'
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
name: test-type-completeness
runs-on: ubuntu-latest
steps:
- uses: Bibo-Joshi/pyright-type-completeness@1.0.0
- uses: Bibo-Joshi/pyright-type-completeness@1.0.1
with:
package-name: telegram
python-version: 3.12
@@ -0,0 +1,33 @@
name: Check Type Completeness Monthly Run
on:
schedule:
# Run first friday of the month at 03:17 - odd time to spread load on GitHub Actions
- cron: '17 3 1-7 * 5'
jobs:
test-type-completeness:
name: test-type-completeness
runs-on: ubuntu-latest
steps:
- uses: Bibo-Joshi/pyright-type-completeness@1.0.1
id: pyright-type-completeness
with:
package-name: telegram
python-version: 3.12
pyright-version: ~=1.1.367
- name: Check Output
uses: jannekem/run-python-script-action@v1
env:
TYPE_COMPLETENESS: ${{ steps.pyright-type-completeness.outputs.base-completeness-score }}
with:
script: |
import os
completeness = float(os.getenv("TYPE_COMPLETENESS"))
if completeness >= 1:
exit(0)
text = f"Type Completeness Decreased to {completeness}. ❌"
error(text)
set_summary(text)
exit(1)
+18 -16
View File
@@ -20,7 +20,7 @@ jobs:
runs-on: ${{matrix.os}}
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13.0-rc.1']
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
os: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: False
steps:
@@ -37,7 +37,7 @@ jobs:
python -W ignore -m pip install -U pytest-cov
python -W ignore -m pip install .
python -W ignore -m pip install -r requirements-unit-tests.txt
python -W ignore -m pip install pytest-xdist[psutil]
python -W ignore -m pip install pytest-xdist
- name: Test with pytest
# We run 4 different suites here
@@ -57,21 +57,17 @@ jobs:
# - without socks support
# - without http2 support
TO_TEST="test_no_passport.py or test_datetime.py or test_defaults.py or test_jobqueue.py or test_applicationbuilder.py or test_ratelimiter.py or test_updater.py or test_callbackdatacache.py or test_request.py"
pytest -v --cov -k "${TO_TEST}"
# Rerun only failed tests (--lf), and don't run any tests if none failed (--lfnf=none)
pytest -v --cov --cov-append -k "${TO_TEST}" --lf --lfnf=none --junit-xml=.test_report_no_optionals.xml
# No tests were selected, convert returned status code to 0
opt_dep_status=$(( $? == 5 ? 0 : $? ))
pytest -v --cov -k "${TO_TEST}" --junit-xml=.test_report_no_optionals_junit.xml
opt_dep_status=$?
# Test the rest
export TEST_WITH_OPT_DEPS='true'
pip install .[all]
# `-n auto --dist loadfile` uses pytest-xdist to run each test file on a different CPU
# worker. Increasing number of workers has little effect on test duration, but it seems
# to increase flakyness, specially on python 3.7 with --dist=loadgroup.
pytest -v --cov --cov-append -n auto --dist loadfile
pytest -v --cov --cov-append -n auto --dist loadfile --lf --lfnf=none --junit-xml=.test_report_optionals.xml
main_status=$(( $? == 5 ? 0 : $? ))
# `-n auto --dist worksteal` uses pytest-xdist to run tests on multiple CPU
# workers. Increasing number of workers has little effect on test duration, but it seems
# to increase flakyness.
pytest -v --cov --cov-append -n auto --dist worksteal --junit-xml=.test_report_optionals_junit.xml
main_status=$?
# exit with non-zero status if any of the two pytest runs failed
exit $(( ${opt_dep_status} || ${main_status} ))
env:
@@ -87,13 +83,19 @@ jobs:
if: always() # always run, even if tests fail
with:
paths: |
.test_report_no_optionals.xml
.test_report_optionals.xml
.test_report_no_optionals_junit.xml
.test_report_optionals_junit.xml
- name: Submit coverage
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
env_vars: OS,PYTHON
name: ${{ matrix.os }}-${{ matrix.python-version }}
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
- name: Upload test results to Codecov
uses: codecov/test-results-action@v1
if: ${{ !cancelled() }}
with:
files: .test_report_no_optionals_junit.xml,.test_report_optionals_junit.xml
token: ${{ secrets.CODECOV_TOKEN }}
+5 -5
View File
@@ -16,7 +16,7 @@ repos:
- tornado~=6.4
- APScheduler~=3.10.4
- cachetools>=5.3.3,<5.5.0
- aiolimiter~=1.1.0
- aiolimiter~=1.1,<1.3
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.4.2
hooks:
@@ -29,7 +29,7 @@ repos:
hooks:
- id: flake8
- repo: https://github.com/PyCQA/pylint
rev: v3.2.4
rev: v3.3.2
hooks:
- id: pylint
files: ^(?!(tests|docs)).*\.py$
@@ -38,7 +38,7 @@ repos:
- tornado~=6.4
- APScheduler~=3.10.4
- cachetools>=5.3.3,<5.5.0
- aiolimiter~=1.1.0
- aiolimiter~=1.1,<1.3
- . # this basically does `pip install -e .`
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.1
@@ -54,7 +54,7 @@ repos:
- tornado~=6.4
- APScheduler~=3.10.4
- cachetools>=5.3.3,<5.5.0
- aiolimiter~=1.1.0
- aiolimiter~=1.1,<1.3
- . # this basically does `pip install -e .`
- id: mypy
name: mypy-examples
@@ -72,7 +72,7 @@ repos:
hooks:
- id: pyupgrade
args:
- --py38-plus
- --py39-plus
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
+4
View File
@@ -23,6 +23,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `Abdelrahman <https://github.com/aelkheir>`_
- `Abshar <https://github.com/abxhr>`_
- `Abubakar Alaya <https://github.com/Ecode2>`_
- `Alateas <https://github.com/alateas>`_
- `Ales Dokshanin <https://github.com/alesdokshanin>`_
- `Alexandre <https://github.com/xTudoS>`_
@@ -80,6 +81,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `LRezende <https://github.com/lrezende>`_
- `Luca Bellanti <https://github.com/Trifase>`_
- `Lucas Molinari <https://github.com/lucasmolinari>`_
- `Luis Pérez <https://github.com/nemacysts>`_
- `macrojames <https://github.com/macrojames>`_
- `Matheus Lemos <https://github.com/mlemosf>`_
- `Michael Dix <https://github.com/Eisberge>`_
@@ -110,11 +112,13 @@ The following wonderful people contributed directly or indirectly to this projec
- `Rahiel Kasim <https://github.com/rahiel>`_
- `Riko Naka <https://github.com/rikonaka>`_
- `Rizlas <https://github.com/rizlas>`_
- `Snehashish Biswas <https://github.com/Snehashish06>`_
- `Sahil Sharma <https://github.com/sahilsharma811>`_
- `Sam Mosleh <https://github.com/sam-mosleh>`_
- `Sascha <https://github.com/saschalalala>`_
- `Shelomentsev D <https://github.com/shelomentsevd>`_
- `Shivam Saini <https://github.com/shivamsn97>`_
- `Siloé Garcez <https://github.com/roast-lord>`_
- `Simon Schürrle <https://github.com/SitiSchu>`_
- `sooyhwang <https://github.com/sooyhwang>`_
- `syntx <https://github.com/syntx>`_
+124
View File
@@ -4,6 +4,130 @@
Changelog
=========
Version 21.9
============
*Released 2024-12-07*
This is the technical changelog for version 21.9. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
Major Changes
-------------
- Full Support for Bot API 8.1 (:pr:`4594` closes :issue:`4592`)
Minor Changes
-------------
- Use ``MessageLimit.DEEP_LINK_LENGTH`` in ``helpers.create_deep_linked_url`` (:pr:`4597` by `nemacysts <https://github.com/nemacysts>`_)
- Allow ``Sequence`` Input for ``allowed_updates`` in ``Application`` and ``Updater`` Methods (:pr:`4589` by `nemacysts <https://github.com/nemacysts>`_)
Dependency Updates
------------------
- Update ``aiolimiter`` requirement from ~=1.1.0 to >=1.1,<1.3 (:pr:`4595`)
- Bump ``pytest`` from 8.3.3 to 8.3.4 (:pr:`4596`)
- Bump ``codecov/codecov-action`` from 4 to 5 (:pr:`4585`)
- Bump ``pylint`` to v3.3.2 to Improve Python 3.13 Support (:pr:`4590` by `nemacysts <https://github.com/nemacysts>`_)
- Bump ``srvaroa/labeler`` from 1.11.1 to 1.12.0 (:pr:`4586`)
Version 21.8
============
*Released 2024-12-01*
This is the technical changelog for version 21.8. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
Major Changes
-------------
- Full Support for Bot API 8.0 (:pr:`4568`, :pr:`4566` closes :issue:`4567`, :pr:`4572`, :pr:`4571`, :pr:`4570`, :pr:`4576`, :pr:`4574`)
Documentation Improvements
--------------------------
- Documentation Improvements (:pr:`4565` by `Snehashish06 <https://github.com/Snehashish06>`_, :pr:`4573`)
Version 21.7
============
*Released 2024-11-04*
This is the technical changelog for version 21.7. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
Major Changes
-------------
- Full Support for Bot API 7.11 (:pr:`4546` closes :issue:`4543`)
- Add ``Message.reply_paid_media`` (:pr:`4551`)
- Drop Support for Python 3.8 (:pr:`4398` by `elpekenin <https://github.com/elpekenin>`_)
Minor Changes
-------------
- Allow ``Sequence`` in ``Application.add_handlers`` (:pr:`4531` by `roast-lord <https://github.com/roast-lord>`_ closes :issue:`4530`)
- Improve Exception Handling in ``File.download_*`` (:pr:`4542`)
- Use Stable Python 3.13 Release in Test Suite (:pr:`4535`)
Documentation Improvements
--------------------------
- Documentation Improvements (:pr:`4536` by `Ecode2 <https://github.com/Ecode2>`_, :pr:`4556`)
- Fix Linkcheck Workflow (:pr:`4545`)
- Use ``sphinx-build-compatibility`` to Keep Sphinx Compatibility (:pr:`4492`)
Internal Changes
----------------
- Improve Test Instability Caused by ``Message`` Fixtures (:pr:`4507`)
- Stabilize Some Flaky Tests (:pr:`4500`)
- Reduce Creation of HTTP Clients in Tests (:pr:`4493`)
- Update ``pytest-xdist`` Usage (:pr:`4491`)
- Fix Failing Tests by Making Them Independent (:pr:`4494`)
- Introduce Codecov's Test Analysis (:pr:`4487`)
- Maintenance Work on ``Bot`` Tests (:pr:`4489`)
- Introduce ``conftest.py`` for File Related Tests (:pr:`4488`)
- Update Issue Templates to Use Issue Types (:pr:`4553`)
- Update Automation to Label Changes (:pr:`4552`)
Dependency Updates
------------------
- Bump ``srvaroa/labeler`` from 1.11.0 to 1.11.1 (:pr:`4549`)
- Bump ``sphinx`` from 8.0.2 to 8.1.3 (:pr:`4532`)
- Bump ``sphinxcontrib-mermaid`` from 0.9.2 to 1.0.0 (:pr:`4529`)
- Bump ``srvaroa/labeler`` from 1.10.1 to 1.11.0 (:pr:`4509`)
- Bump ``Bibo-Joshi/pyright-type-completeness`` from 1.0.0 to 1.0.1 (:pr:`4510`)
Version 21.6
============
*Released 2024-09-19*
This is the technical changelog for version 21.6. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
New Features
------------
- Full Support for Bot API 7.10 (:pr:`4461` closes :issue:`4459`, :pr:`4460`, :pr:`4463` by `aelkheir <https://github.com/aelkheir>`_, :pr:`4464`)
- Add Parameter ``httpx_kwargs`` to ``HTTPXRequest`` (:pr:`4451` closes :issue:`4424`)
Minor Changes
-------------
- Improve Type Completeness (:pr:`4466`)
Internal Changes
----------------
- Update Python 3.13 Test Suite to RC2 (:pr:`4471`)
- Enforce the ``offline_bot`` Fixture in ``Test*WithoutRequest`` (:pr:`4465`)
- Make Tests for ``telegram.ext`` Independent of Networking (:pr:`4454`)
- Rename Testing Base Classes (:pr:`4453`)
Dependency Updates
------------------
- Bump ``pytest`` from 8.3.2 to 8.3.3 (:pr:`4475`)
Version 21.5
============
+7 -7
View File
@@ -11,7 +11,7 @@
:target: https://pypi.org/project/python-telegram-bot/
:alt: Supported Python versions
.. image:: https://img.shields.io/badge/Bot%20API-7.9-blue?logo=telegram
.. image:: https://img.shields.io/badge/Bot%20API-8.1-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API version
@@ -70,7 +70,7 @@ Introduction
This library provides a pure Python, asynchronous interface for the
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
It's compatible with Python versions **3.8+**.
It's compatible with Python versions **3.9+**.
In addition to the pure API implementation, this library features several convenience methods and shortcuts as well as a number of high-level classes to
make the development of bots easy and straightforward. These classes are contained in the
@@ -81,7 +81,7 @@ After installing_ the library, be sure to check out the section on `working with
Telegram API support
~~~~~~~~~~~~~~~~~~~~
All types and methods of the Telegram Bot API **7.9** are natively supported by this library.
All types and methods of the Telegram Bot API **8.1** are natively supported by this library.
In addition, Bot API functionality not yet natively included can still be used as described `in our wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Bot-API-Forward-Compatibility>`_.
Notable Features
@@ -121,7 +121,7 @@ To enable you to verify that a release file that you downloaded was indeed provi
Starting with v21.4, all releases are signed via `sigstore <https://www.sigstore.dev>`_.
The corresponding signature files are uploaded to the `GitHub releases page`_.
To verify the signature, please install the `sigstore Python client <https://pypi.org/project/sigstore/>`_ and follow the instructions for `verifying signatures from GitHub Actions <https://github.com/sigstore/sigstore-python#signatures-from-github-actions>`_. As input for the ``--repository`` parameter, please use the value ``python-telegram-bot/python-telegram-bot``.
To verify the signature, please install the `sigstore Python client <https://pypi.org/project/sigstore/>`_ and follow the instructions for `verifying signatures from GitHub Actions <https://github.com/sigstore/sigstore-python?tab=readme-ov-file>`_. As input for the ``--repository`` parameter, please use the value ``python-telegram-bot/python-telegram-bot``.
Earlier releases are signed with a GPG key.
The signatures are uploaded to both the `GitHub releases page`_ and the `PyPI project <https://pypi.org/project/python-telegram-bot/>`_ and end with a suffix ``.asc``.
@@ -155,7 +155,7 @@ PTB can be installed with optional dependencies:
* ``pip install "python-telegram-bot[passport]"`` installs the `cryptography>=39.0.1 <https://cryptography.io/en/stable>`_ library. Use this, if you want to use Telegram Passport related functionality.
* ``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[rate-limiter]"`` installs `aiolimiter~=1.1,<1.3 <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.4 <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.3,<5.6.0 <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``.
@@ -230,6 +230,6 @@ License
-------
You may copy, distribute and modify the software provided that modifications are described and licensed for free under `LGPL-3 <https://www.gnu.org/licenses/lgpl-3.0.html>`_.
Derivatives works (including modifications or anything statically linked to the library) can only be redistributed under LGPL-3, but applications that use the library don't have to be.
Derivative works (including modifications or anything statically linked to the library) can only be redistributed under LGPL-3, but applications that use the library don't have to be.
.. _`GitHub releases page`: https://github.com/python-telegram-bot/python-telegram-bot/releases>
.. _`GitHub releases page`: https://github.com/python-telegram-bot/python-telegram-bot/releases
+2 -1
View File
@@ -20,7 +20,8 @@ import inspect
import re
import typing
from collections import defaultdict
from typing import Any, Iterator, Union
from collections.abc import Iterator
from typing import Any, Union
import telegram
import telegram.ext
+1 -2
View File
@@ -16,7 +16,6 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import inspect
from typing import List
keyword_args = [
"Keyword Arguments:",
@@ -85,7 +84,7 @@ get_updates_read_timeout_addition = [
]
def find_insert_pos_for_kwargs(lines: List[str]) -> int:
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"):
+1 -2
View File
@@ -21,7 +21,6 @@ https://github.com/sphinx-doc/sphinx/issues/1556 is closed
"""
import subprocess
from pathlib import Path
from typing import Dict, Tuple
from sphinx.util import logging
@@ -32,7 +31,7 @@ sphinx_logger = logging.getLogger(__name__)
# must be a module-level variable so that it can be written to by the `autodoc-process-docstring`
# event handler in `sphinx_hooks.py`
LINE_NUMBERS: Dict[str, Tuple[Path, int, int]] = {}
LINE_NUMBERS: dict[str, tuple[Path, int, int]] = {}
def _git_branch() -> str:
+5
View File
@@ -187,6 +187,11 @@ def autodoc_process_bases(app, name, obj, option, bases: list) -> None:
bases[idx] = ":class:`enum.IntEnum`"
continue
if "FloatEnum" in base:
bases[idx] = ":class:`enum.Enum`"
bases.insert(0, ":class:`float`")
continue
# Drop generics (at least for now)
if base.endswith("]"):
base = base.split("[", maxsplit=1)[0]
+5 -3
View File
@@ -1,7 +1,9 @@
sphinx==8.0.2
sphinx==8.1.3
furo==2024.8.6
furo-sphinx-search @ git+https://github.com/harshil21/furo-sphinx-search@v0.2.0.1
sphinx-paramlinks==0.6.0
sphinxcontrib-mermaid==0.9.2
sphinxcontrib-mermaid==1.0.0
sphinx-copybutton==0.5.2
sphinx-inline-tabs==2023.4.21
sphinx-inline-tabs==2023.4.21
# Temporary. See #4387
sphinx-build-compatibility @ git+https://github.com/readthedocs/sphinx-build-compatibility.git@58aabc5f207c6c2421f23d3578adc0b14af57047
+5 -1
View File
@@ -30,7 +30,7 @@ version = telegram.__version__
release = telegram.__version__
# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = "8.0.2"
needs_sphinx = "8.1.3"
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@@ -48,6 +48,10 @@ extensions = [
"sphinx_search.extension",
]
# Temporary. See #4387
if os.environ.get("READTHEDOCS", "") == "True":
extensions.append("sphinx_build_compatibility.extension")
# For shorter links to Wiki in docstrings
extlinks = {
"wiki": ("https://github.com/python-telegram-bot/python-telegram-bot/wiki/%s", "%s"),
+30 -5
View File
@@ -25,6 +25,8 @@
- Used for sending documents
* - :meth:`~telegram.Bot.send_game`
- Used for sending a game
* - :meth:`~telegram.Bot.send_gift`
- Used for sending a gift
* - :meth:`~telegram.Bot.send_invoice`
- Used for sending an invoice
* - :meth:`~telegram.Bot.send_location`
@@ -151,6 +153,8 @@
- Used for setting a chat title
* - :meth:`~telegram.Bot.set_chat_description`
- Used for setting the description of a chat
* - :meth:`~telegram.Bot.set_user_emoji_status`
- Used for setting the users status emoji
* - :meth:`~telegram.Bot.pin_chat_message`
- Used for pinning a message
* - :meth:`~telegram.Bot.unpin_chat_message`
@@ -355,7 +359,7 @@
.. raw:: html
<details>
<summary>Miscellaneous</summary>
<summary>Payments and Stars</summary>
.. list-table::
:align: left
@@ -363,18 +367,39 @@
* - :meth:`~telegram.Bot.create_invoice_link`
- Used to generate an HTTP link for an invoice
* - :meth:`~telegram.Bot.edit_user_star_subscription`
- Used for editing a user's star subscription
* - :meth:`~telegram.Bot.get_star_transactions`
- Used for obtaining the bot's Telegram Stars transactions
* - :meth:`~telegram.Bot.refund_star_payment`
- Used for refunding a payment in Telegram Stars
.. raw:: html
</details>
<br>
.. raw:: html
<details>
<summary>Miscellaneous</summary>
.. list-table::
:align: left
:widths: 1 4
* - :meth:`~telegram.Bot.close`
- Used for closing server instance when switching to another local server
* - :meth:`~telegram.Bot.log_out`
- Used for logging out from cloud Bot API server
* - :meth:`~telegram.Bot.get_file`
- Used for getting basic info about a file
* - :meth:`~telegram.Bot.get_available_gifts`
- Used for getting information about gifts available for sending
* - :meth:`~telegram.Bot.get_me`
- Used for getting basic information about the bot
* - :meth:`~telegram.Bot.get_star_transactions`
- Used for obtaining the bot's Telegram Stars transactions
* - :meth:`~telegram.Bot.refund_star_payment`
- Used for refunding a payment in Telegram Stars
* - :meth:`~telegram.Bot.save_prepared_inline_message`
- Used for storing a message to be sent by a user of a Mini App
.. raw:: html
+6
View File
@@ -0,0 +1,6 @@
AffiliateInfo
=============
.. autoclass:: telegram.AffiliateInfo
:members:
:show-inheritance:
+2 -1
View File
@@ -29,6 +29,7 @@ Available Types
telegram.chat
telegram.chatadministratorrights
telegram.chatbackground
telegram.copytextbutton
telegram.backgroundtype
telegram.backgroundtypefill
telegram.backgroundtypewallpaper
@@ -92,7 +93,6 @@ Available Types
telegram.inputpaidmediaphoto
telegram.inputpaidmediavideo
telegram.inputpolloption
telegram.inputsticker
telegram.keyboardbutton
telegram.keyboardbuttonpolltype
telegram.keyboardbuttonrequestchat
@@ -120,6 +120,7 @@ Available Types
telegram.paidmediainfo
telegram.paidmediaphoto
telegram.paidmediapreview
telegram.paidmediapurchased
telegram.paidmediavideo
telegram.photosize
telegram.poll
+1 -1
View File
@@ -5,5 +5,5 @@ telegram.constants Module
:members:
:show-inheritance:
:no-undoc-members:
:inherited-members: Enum, EnumMeta, str, int
:inherited-members: Enum, EnumMeta, str, int, float
:exclude-members: __format__, __new__, __repr__, __str__
+6
View File
@@ -0,0 +1,6 @@
CopyTextButton
==============
.. autoclass:: telegram.CopyTextButton
:members:
:show-inheritance:
@@ -18,6 +18,7 @@ Handlers
telegram.ext.inlinequeryhandler
telegram.ext.messagehandler
telegram.ext.messagereactionhandler
telegram.ext.paidmediapurchasedhandler
telegram.ext.pollanswerhandler
telegram.ext.pollhandler
telegram.ext.precheckoutqueryhandler
@@ -0,0 +1,6 @@
PaidMediaPurchasedHandler
=========================
.. autoclass:: telegram.ext.PaidMediaPurchasedHandler
:members:
:show-inheritance:
+6
View File
@@ -0,0 +1,6 @@
Gift
====
.. autoclass:: telegram.Gift
:members:
:show-inheritance:
+6
View File
@@ -0,0 +1,6 @@
Gifts
=====
.. autoclass:: telegram.Gifts
:members:
:show-inheritance:
+1
View File
@@ -42,3 +42,4 @@ To enable this option, send the ``/setinline`` command to `@BotFather <https://t
telegram.inputvenuemessagecontent
telegram.inputcontactmessagecontent
telegram.inputinvoicemessagecontent
telegram.preparedinlinemessage
@@ -0,0 +1,6 @@
PaidMediaPurchased
==================
.. autoclass:: telegram.PaidMediaPurchased
:members:
:show-inheritance:
+3
View File
@@ -9,6 +9,7 @@ Your bot can accept payments from Telegram users. Please see the `introduction t
.. toctree::
:titlesonly:
telegram.affiliateinfo
telegram.invoice
telegram.labeledprice
telegram.orderinfo
@@ -25,7 +26,9 @@ Your bot can accept payments from Telegram users. Please see the `introduction t
telegram.startransactions
telegram.successfulpayment
telegram.transactionpartner
telegram.transactionpartneraffiliateprogram
telegram.transactionpartnerfragment
telegram.transactionpartnerother
telegram.transactionpartnertelegramads
telegram.transactionpartnertelegramapi
telegram.transactionpartneruser
@@ -0,0 +1,6 @@
PreparedInlineMessage
=====================
.. autoclass:: telegram.PreparedInlineMessage
:members:
:show-inheritance:
+3
View File
@@ -6,6 +6,9 @@ The following methods and objects allow your bot to handle stickers and sticker
.. toctree::
:titlesonly:
telegram.gift
telegram.gifts
telegram.inputsticker
telegram.maskposition
telegram.sticker
telegram.stickerset
@@ -0,0 +1,6 @@
TransactionPartnerAffiliateProgram
===================================
.. autoclass:: telegram.TransactionPartnerAffiliateProgram
:members:
:show-inheritance:
@@ -4,4 +4,4 @@ TransactionPartnerFragment
.. autoclass:: telegram.TransactionPartnerFragment
:members:
:show-inheritance:
:inherited-members: TelegramObject
:inherited-members: TransactionPartner
@@ -4,4 +4,4 @@ TransactionPartnerOther
.. autoclass:: telegram.TransactionPartnerOther
:members:
:show-inheritance:
:inherited-members: TelegramObject
:inherited-members: TransactionPartner
@@ -4,4 +4,4 @@ TransactionPartnerTelegramAds
.. autoclass:: telegram.TransactionPartnerTelegramAds
:members:
:show-inheritance:
:inherited-members: TelegramObject
:inherited-members: TransactionPartner
@@ -0,0 +1,7 @@
TransactionPartnerTelegramApi
=============================
.. autoclass:: telegram.TransactionPartnerTelegramApi
:members:
:show-inheritance:
:inherited-members: TransactionPartner
@@ -4,4 +4,4 @@ TransactionPartnerUser
.. autoclass:: telegram.TransactionPartnerUser
:members:
:show-inheritance:
:inherited-members: TelegramObject
:inherited-members: TransactionPartner
+4
View File
@@ -93,3 +93,7 @@
.. |show_cap_above_med| replace:: :obj:`True`, if the caption must be shown above the message media.
.. |tg_stars| replace:: `Telegram Stars <https://t.me/BotNews/90>`__
.. |allow_paid_broadcast| replace:: Pass True to allow up to :tg-const:`telegram.constants.FloodLimit.PAID_MESSAGES_PER_SECOND` messages per second, ignoring `broadcasting limits <https://core.telegram.org/bots/faq#how-can-i-message-all-of-my-bot-39s-subscribers-at-once>`__ for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance.
.. |tz-naive-dtms| replace:: For timezone naive :obj:`datetime.datetime` objects, the default timezone of the bot will be used, which is UTC unless :attr:`telegram.ext.Defaults.tzinfo` is used.
+4 -4
View File
@@ -12,7 +12,7 @@ To use arbitrary callback data, you must install PTB via
`pip install "python-telegram-bot[callback-data]"`
"""
import logging
from typing import List, Tuple, cast
from typing import cast
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import (
@@ -36,7 +36,7 @@ logger = logging.getLogger(__name__)
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Sends a message with 5 inline buttons attached."""
number_list: List[int] = []
number_list: list[int] = []
await update.message.reply_text("Please choose:", reply_markup=build_keyboard(number_list))
@@ -55,7 +55,7 @@ async def clear(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.effective_message.reply_text("All clear!")
def build_keyboard(current_list: List[int]) -> InlineKeyboardMarkup:
def build_keyboard(current_list: list[int]) -> InlineKeyboardMarkup:
"""Helper function to build the next inline keyboard."""
return InlineKeyboardMarkup.from_column(
[InlineKeyboardButton(str(i), callback_data=(i, current_list)) for i in range(1, 6)]
@@ -69,7 +69,7 @@ async def list_button(update: Update, context: ContextTypes.DEFAULT_TYPE) -> Non
# Get the data from the callback_data.
# If you're using a type checker like MyPy, you'll have to use typing.cast
# to make the checker get the expected type of the callback_data
number, number_list = cast(Tuple[int, List[int]], query.data)
number, number_list = cast(tuple[int, list[int]], query.data)
# append the number to the list
number_list.append(number)
+2 -2
View File
@@ -12,7 +12,7 @@ bot.
"""
import logging
from typing import Optional, Tuple
from typing import Optional
from telegram import Chat, ChatMember, ChatMemberUpdated, Update
from telegram.constants import ParseMode
@@ -37,7 +37,7 @@ logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
def extract_status_change(chat_member_update: ChatMemberUpdated) -> Optional[Tuple[bool, bool]]:
def extract_status_change(chat_member_update: ChatMemberUpdated) -> Optional[tuple[bool, bool]]:
"""Takes a ChatMemberUpdated instance and extracts whether the 'old_chat_member' was a member
of the chat and whether the 'new_chat_member' is a member of the chat. Returns None, if
the status didn't change.
+3 -3
View File
@@ -12,7 +12,7 @@ bot.
import logging
from collections import defaultdict
from typing import DefaultDict, Optional, Set
from typing import Optional
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.constants import ParseMode
@@ -40,7 +40,7 @@ class ChatData:
"""Custom class for chat_data. Here we store data per message."""
def __init__(self) -> None:
self.clicks_per_message: DefaultDict[int, int] = defaultdict(int)
self.clicks_per_message: defaultdict[int, int] = defaultdict(int)
# The [ExtBot, dict, ChatData, dict] is for type checkers like mypy
@@ -57,7 +57,7 @@ class CustomContext(CallbackContext[ExtBot, dict, ChatData, dict]):
self._message_id: Optional[int] = None
@property
def bot_user_ids(self) -> Set[int]:
def bot_user_ids(self) -> set[int]:
"""Custom shortcut to access a value stored in the bot_data dict"""
return self.bot_data.setdefault("user_ids", set())
+1 -2
View File
@@ -15,7 +15,6 @@ bot.
"""
import logging
from typing import Dict
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
from telegram.ext import (
@@ -46,7 +45,7 @@ reply_keyboard = [
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
def facts_to_str(user_data: Dict[str, str]) -> str:
def facts_to_str(user_data: dict[str, str]) -> str:
"""Helper function for formatting the gathered user info."""
facts = [f"{key} - {value}" for key, value in user_data.items()]
return "\n".join(facts).join(["\n", "\n"])
+5 -5
View File
@@ -15,7 +15,7 @@ bot.
"""
import logging
from typing import Any, Dict, Tuple
from typing import Any
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import (
@@ -66,7 +66,7 @@ END = ConversationHandler.END
# Helper
def _name_switcher(level: str) -> Tuple[str, str]:
def _name_switcher(level: str) -> tuple[str, str]:
if level == PARENTS:
return "Father", "Mother"
return "Brother", "Sister"
@@ -122,7 +122,7 @@ async def adding_self(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str
async def show_data(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str:
"""Pretty print gathered data."""
def pretty_print(data: Dict[str, Any], level: str) -> str:
def pretty_print(data: dict[str, Any], level: str) -> str:
people = data.get(level)
if not people:
return "\nNo information yet."
@@ -371,8 +371,8 @@ def main() -> None:
entry_points=[CommandHandler("start", start)],
states={
SHOWING: [CallbackQueryHandler(start, pattern="^" + str(END) + "$")],
SELECTING_ACTION: selection_handlers,
SELECTING_LEVEL: selection_handlers,
SELECTING_ACTION: selection_handlers, # type: ignore[dict-item]
SELECTING_LEVEL: selection_handlers, # type: ignore[dict-item]
DESCRIBING_SELF: [description_conv],
STOPPING: [CommandHandler("start", start)],
},
+39 -40
View File
@@ -2,7 +2,7 @@
# 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."""
"""Basic example for a bot that can receive payments from users."""
import logging
@@ -26,36 +26,36 @@ logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
# Insert the token from your payment provider.
# In order to get a provider_token see https://core.telegram.org/bots/payments#getting-a-token
PAYMENT_PROVIDER_TOKEN = "PAYMENT_PROVIDER_TOKEN"
async def start_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Displays info on how to use the bot."""
"""Provides instructions on how to use the bot."""
msg = (
"Use /shipping to get an invoice for shipping-payment, or /noshipping for an "
"Use /shipping to receive an invoice with shipping included, or /noshipping for an "
"invoice without shipping."
)
await update.message.reply_text(msg)
async def start_with_shipping_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Sends an invoice with shipping-payment."""
"""Sends an invoice which triggers a shipping query."""
chat_id = update.message.chat_id
title = "Payment Example"
description = "Payment Example using python-telegram-bot"
# select a payload just for you to recognize its the donation from your bot
description = "Example of a payment process using the python-telegram-bot library."
# Unique payload to identify this payment request as being from your bot
payload = "Custom-Payload"
# In order to get a provider_token see https://core.telegram.org/bots/payments#getting-a-token
# Set up the currency.
# List of supported currencies: https://core.telegram.org/bots/payments#supported-currencies
currency = "USD"
# price in dollars
# Price in dollars
price = 1
# price * 100 so as to include 2 decimal points
# check https://core.telegram.org/bots/payments#supported-currencies for more details
# Convert price to cents from dollars.
prices = [LabeledPrice("Test", price * 100)]
# optionally pass need_name=True, need_phone_number=True,
# need_email=True, need_shipping_address=True, is_flexible=True
# Optional parameters like need_shipping_address and is_flexible trigger extra user prompts
# https://docs.python-telegram-bot.org/en/stable/telegram.bot.html#telegram.Bot.send_invoice
await context.bot.send_invoice(
chat_id,
title,
@@ -75,17 +75,16 @@ async def start_with_shipping_callback(update: Update, context: ContextTypes.DEF
async def start_without_shipping_callback(
update: Update, context: ContextTypes.DEFAULT_TYPE
) -> None:
"""Sends an invoice without shipping-payment."""
"""Sends an invoice without requiring shipping details."""
chat_id = update.message.chat_id
title = "Payment Example"
description = "Payment Example using python-telegram-bot"
# select a payload just for you to recognize its the donation from your bot
description = "Example of a payment process using the python-telegram-bot library."
# Unique payload to identify this payment request as being from your bot
payload = "Custom-Payload"
# In order to get a provider_token see https://core.telegram.org/bots/payments#getting-a-token
currency = "USD"
# price in dollars
# Price in dollars
price = 1
# price * 100 so as to include 2 decimal points
# Convert price to cents from dollars.
prices = [LabeledPrice("Test", price * 100)]
# optionally pass need_name=True, need_phone_number=True,
@@ -96,65 +95,65 @@ async def start_without_shipping_callback(
async def shipping_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Answers the ShippingQuery with ShippingOptions"""
"""Handles the ShippingQuery with available shipping options."""
query = update.shipping_query
# check the payload, is this from your bot?
# Verify if the payload matches, ensure it's from your bot
if query.invoice_payload != "Custom-Payload":
# answer False pre_checkout_query
# If not, respond with an error
await query.answer(ok=False, error_message="Something went wrong...")
return
# First option has a single LabeledPrice
# Define available shipping options
# First option with a single price entry
options = [ShippingOption("1", "Shipping Option A", [LabeledPrice("A", 100)])]
# second option has an array of LabeledPrice objects
# Second option with multiple price entries
price_list = [LabeledPrice("B1", 150), LabeledPrice("B2", 200)]
options.append(ShippingOption("2", "Shipping Option B", price_list))
await query.answer(ok=True, shipping_options=options)
# after (optional) shipping, it's the pre-checkout
# After (optional) shipping, process the pre-checkout step
async def precheckout_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Answers the PreQecheckoutQuery"""
"""Responds to the PreCheckoutQuery as the final confirmation for checkout."""
query = update.pre_checkout_query
# check the payload, is this from your bot?
# Verify if the payload matches, ensure it's from your bot
if query.invoice_payload != "Custom-Payload":
# answer False pre_checkout_query
# If not, respond with an error
await query.answer(ok=False, error_message="Something went wrong...")
else:
await query.answer(ok=True)
# finally, after contacting the payment provider...
# Final callback after successful payment
async def successful_payment_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Confirms the successful payment."""
# do something after successfully receiving payment?
await update.message.reply_text("Thank you for your payment!")
"""Acknowledges successful payment and thanks the user."""
await update.message.reply_text("Thank you for your payment.")
def main() -> None:
"""Run the bot."""
"""Starts the bot and sets up handlers."""
# Create the Application and pass it your bot's token.
application = Application.builder().token("TOKEN").build()
# simple start function
# Start command to display usage instructions
application.add_handler(CommandHandler("start", start_callback))
# Add command handler to start the payment invoice
# Command handlers for starting the payment process
application.add_handler(CommandHandler("shipping", start_with_shipping_callback))
application.add_handler(CommandHandler("noshipping", start_without_shipping_callback))
# Optional handler if your product requires shipping
# Handler for shipping query (if product requires shipping)
application.add_handler(ShippingQueryHandler(shipping_callback))
# Pre-checkout handler to final check
# Pre-checkout handler for verifying payment details.
application.add_handler(PreCheckoutQueryHandler(precheckout_callback))
# Success! Notify your user!
# Handler for successful payment. Notify the user that the payment was successful.
application.add_handler(
MessageHandler(filters.SUCCESSFUL_PAYMENT, successful_payment_callback)
)
# Run the bot until the user presses Ctrl-C
# Start polling for updates until interrupted (CTRL+C)
application.run_polling(allowed_updates=Update.ALL_TYPES)
+1 -2
View File
@@ -15,7 +15,6 @@ bot.
"""
import logging
from typing import Dict
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
from telegram.ext import (
@@ -47,7 +46,7 @@ reply_keyboard = [
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
def facts_to_str(user_data: Dict[str, str]) -> str:
def facts_to_str(user_data: dict[str, str]) -> str:
"""Helper function for formatting the gathered user info."""
facts = [f"{key} - {value}" for key, value in user_data.items()]
return "\n".join(facts).join(["\n", "\n"])
+5 -5
View File
@@ -8,7 +8,7 @@ dynamic = ["version"]
name = "python-telegram-bot"
description = "We have made you a wrapper you can't refuse"
readme = "README.rst"
requires-python = ">=3.8"
requires-python = ">=3.9"
license = "LGPL-3.0-only"
license-files = { paths = ["LICENSE", "LICENSE.dual", "LICENSE.lesser"] }
authors = [
@@ -31,7 +31,6 @@ classifiers = [
"Topic :: Internet",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
@@ -87,7 +86,7 @@ passport = [
"cffi >= 1.17.0rc1; python_version > '3.12'"
]
rate-limiter = [
"aiolimiter~=1.1.0",
"aiolimiter>=1.1,<1.3",
]
socks = [
"httpx[socks]",
@@ -150,7 +149,8 @@ enable = ["useless-suppression"]
disable = ["duplicate-code", "too-many-arguments", "too-many-public-methods",
"too-few-public-methods", "broad-exception-caught", "too-many-instance-attributes",
"fixme", "missing-function-docstring", "missing-class-docstring", "too-many-locals",
"too-many-lines", "too-many-branches", "too-many-statements", "cyclic-import"
"too-many-lines", "too-many-branches", "too-many-statements", "cyclic-import",
"too-many-positional-arguments",
]
[tool.pylint.main]
@@ -192,7 +192,7 @@ disallow_untyped_defs = true
disallow_incomplete_defs = true
disallow_untyped_decorators = true
show_error_codes = true
python_version = "3.8"
python_version = "3.9"
# For some files, it's easier to just disable strict-optional all together instead of
# cluttering the code with `# type: ignore`s or stuff like
+1 -1
View File
@@ -4,7 +4,7 @@
build
# For the test suite
pytest==8.3.2
pytest==8.3.4
# needed because pytest doesn't come with native support for coroutines as tests
pytest-asyncio==0.21.2
+22 -1
View File
@@ -20,6 +20,7 @@
__author__ = "devs@python-telegram-bot.org"
__all__ = (
"AffiliateInfo",
"Animation",
"Audio",
"BackgroundFill",
@@ -81,6 +82,7 @@ __all__ = (
"ChatShared",
"ChosenInlineResult",
"Contact",
"CopyTextButton",
"Credentials",
"DataCredentials",
"Dice",
@@ -100,6 +102,8 @@ __all__ = (
"GameHighScore",
"GeneralForumTopicHidden",
"GeneralForumTopicUnhidden",
"Gift",
"Gifts",
"Giveaway",
"GiveawayCompleted",
"GiveawayCreated",
@@ -180,6 +184,7 @@ __all__ = (
"PaidMediaInfo",
"PaidMediaPhoto",
"PaidMediaPreview",
"PaidMediaPurchased",
"PaidMediaVideo",
"PassportData",
"PassportElementError",
@@ -199,6 +204,7 @@ __all__ = (
"PollAnswer",
"PollOption",
"PreCheckoutQuery",
"PreparedInlineMessage",
"ProximityAlertTriggered",
"ReactionCount",
"ReactionType",
@@ -231,9 +237,11 @@ __all__ = (
"TelegramObject",
"TextQuote",
"TransactionPartner",
"TransactionPartnerAffiliateProgram",
"TransactionPartnerFragment",
"TransactionPartnerOther",
"TransactionPartnerTelegramAds",
"TransactionPartnerTelegramApi",
"TransactionPartnerUser",
"Update",
"User",
@@ -329,6 +337,7 @@ from ._chatmember import (
from ._chatmemberupdated import ChatMemberUpdated
from ._chatpermissions import ChatPermissions
from ._choseninlineresult import ChosenInlineResult
from ._copytextbutton import CopyTextButton
from ._dice import Dice
from ._files.animation import Animation
from ._files.audio import Audio
@@ -369,6 +378,7 @@ from ._forumtopic import (
from ._games.callbackgame import CallbackGame
from ._games.game import Game
from ._games.gamehighscore import GameHighScore
from ._gifts import Gift, Gifts
from ._giveaway import Giveaway, GiveawayCompleted, GiveawayCreated, GiveawayWinners
from ._inline.inlinekeyboardbutton import InlineKeyboardButton
from ._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
@@ -401,6 +411,7 @@ from ._inline.inputlocationmessagecontent import InputLocationMessageContent
from ._inline.inputmessagecontent import InputMessageContent
from ._inline.inputtextmessagecontent import InputTextMessageContent
from ._inline.inputvenuemessagecontent import InputVenueMessageContent
from ._inline.preparedinlinemessage import PreparedInlineMessage
from ._keyboardbutton import KeyboardButton
from ._keyboardbuttonpolltype import KeyboardButtonPollType
from ._keyboardbuttonrequest import KeyboardButtonRequestChat, KeyboardButtonRequestUsers
@@ -419,7 +430,14 @@ from ._messageorigin import (
MessageOriginUser,
)
from ._messagereactionupdated import MessageReactionCountUpdated, MessageReactionUpdated
from ._paidmedia import PaidMedia, PaidMediaInfo, PaidMediaPhoto, PaidMediaPreview, PaidMediaVideo
from ._paidmedia import (
PaidMedia,
PaidMediaInfo,
PaidMediaPhoto,
PaidMediaPreview,
PaidMediaPurchased,
PaidMediaVideo,
)
from ._passport.credentials import (
Credentials,
DataCredentials,
@@ -453,6 +471,7 @@ from ._payment.shippingaddress import ShippingAddress
from ._payment.shippingoption import ShippingOption
from ._payment.shippingquery import ShippingQuery
from ._payment.stars import (
AffiliateInfo,
RevenueWithdrawalState,
RevenueWithdrawalStateFailed,
RevenueWithdrawalStatePending,
@@ -460,9 +479,11 @@ from ._payment.stars import (
StarTransaction,
StarTransactions,
TransactionPartner,
TransactionPartnerAffiliateProgram,
TransactionPartnerFragment,
TransactionPartnerOther,
TransactionPartnerTelegramAds,
TransactionPartnerTelegramApi,
TransactionPartnerUser,
)
from ._payment.successfulpayment import SuccessfulPayment
+448 -85
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
# pylint: disable=redefined-builtin
"""This module contains objects representing Telegram bot command scopes."""
from typing import TYPE_CHECKING, Dict, Final, Optional, Type, Union
from typing import TYPE_CHECKING, Final, Optional, Union
from telegram import constants
from telegram._telegramobject import TelegramObject
@@ -91,7 +91,7 @@ class BotCommandScope(TelegramObject):
care of selecting the correct subclass.
Args:
data (Dict[:obj:`str`, ...]): The JSON data.
data (dict[:obj:`str`, ...]): The JSON data.
bot (:class:`telegram.Bot`, optional): The bot associated with this object. Defaults to
:obj:`None`, in which case shortcut methods will not be available.
@@ -107,7 +107,7 @@ class BotCommandScope(TelegramObject):
if not data:
return None
_class_mapping: Dict[str, Type[BotCommandScope]] = {
_class_mapping: dict[str, type[BotCommandScope]] = {
cls.DEFAULT: BotCommandScopeDefault,
cls.ALL_PRIVATE_CHATS: BotCommandScopeAllPrivateChats,
cls.ALL_GROUP_CHATS: BotCommandScopeAllGroupChats,
+11 -10
View File
@@ -19,8 +19,9 @@
# along with this program. If not, see [http://www.gnu.org/licenses/]
"""This module contains the Telegram Business related classes."""
from collections.abc import Sequence
from datetime import datetime
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from typing import TYPE_CHECKING, Optional
from telegram._chat import Chat
from telegram._files.location import Location
@@ -145,7 +146,7 @@ class BusinessMessagesDeleted(TelegramObject):
business_connection_id (:obj:`str`): Unique identifier of the business connection.
chat (:class:`telegram.Chat`): Information about a chat in the business account. The bot
may not have access to the chat or the corresponding user.
message_ids (Tuple[:obj:`int`]): A list of identifiers of the deleted messages in the
message_ids (tuple[:obj:`int`]): A list of identifiers of the deleted messages in the
chat of the business account.
"""
@@ -166,7 +167,7 @@ class BusinessMessagesDeleted(TelegramObject):
super().__init__(api_kwargs=api_kwargs)
self.business_connection_id: str = business_connection_id
self.chat: Chat = chat
self.message_ids: Tuple[int, ...] = parse_sequence_arg(message_ids)
self.message_ids: tuple[int, ...] = parse_sequence_arg(message_ids)
self._id_attrs = (
self.business_connection_id,
@@ -359,37 +360,37 @@ class BusinessOpeningHoursInterval(TelegramObject):
self.opening_minute: int = opening_minute
self.closing_minute: int = closing_minute
self._opening_time: Optional[Tuple[int, int, int]] = None
self._closing_time: Optional[Tuple[int, int, int]] = None
self._opening_time: Optional[tuple[int, int, int]] = None
self._closing_time: Optional[tuple[int, int, int]] = None
self._id_attrs = (self.opening_minute, self.closing_minute)
self._freeze()
def _parse_minute(self, minute: int) -> Tuple[int, int, int]:
def _parse_minute(self, minute: int) -> tuple[int, int, int]:
return (minute // 1440, minute % 1440 // 60, minute % 1440 % 60)
@property
def opening_time(self) -> Tuple[int, int, int]:
def opening_time(self) -> tuple[int, int, int]:
"""Convenience attribute. A :obj:`tuple` parsed from :attr:`opening_minute`. It contains
the `weekday`, `hour` and `minute` in the same ranges as :attr:`datetime.datetime.weekday`,
:attr:`datetime.datetime.hour` and :attr:`datetime.datetime.minute`
Returns:
Tuple[:obj:`int`, :obj:`int`, :obj:`int`]:
tuple[:obj:`int`, :obj:`int`, :obj:`int`]:
"""
if self._opening_time is None:
self._opening_time = self._parse_minute(self.opening_minute)
return self._opening_time
@property
def closing_time(self) -> Tuple[int, int, int]:
def closing_time(self) -> tuple[int, int, int]:
"""Convenience attribute. A :obj:`tuple` parsed from :attr:`closing_minute`. It contains
the `weekday`, `hour` and `minute` in the same ranges as :attr:`datetime.datetime.weekday`,
:attr:`datetime.datetime.hour` and :attr:`datetime.datetime.minute`
Returns:
Tuple[:obj:`int`, :obj:`int`, :obj:`int`]:
tuple[:obj:`int`, :obj:`int`, :obj:`int`]:
"""
if self._closing_time is None:
self._closing_time = self._parse_minute(self.closing_minute)
+6 -3
View File
@@ -18,7 +18,8 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
# pylint: disable=redefined-builtin
"""This module contains an object that represents a Telegram CallbackQuery"""
from typing import TYPE_CHECKING, Final, Optional, Sequence, Tuple, Union
from collections.abc import Sequence
from typing import TYPE_CHECKING, Final, Optional, Union
from telegram import constants
from telegram._files.location import Location
@@ -676,7 +677,7 @@ class CallbackQuery(TelegramObject):
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> Tuple["GameHighScore", ...]:
) -> tuple["GameHighScore", ...]:
"""Shortcut for either::
await update.callback_query.message.get_game_high_score(*args, **kwargs)
@@ -695,7 +696,7 @@ class CallbackQuery(TelegramObject):
Raises :exc:`TypeError` if :attr:`message` is not accessible.
Returns:
Tuple[:class:`telegram.GameHighScore`]
tuple[:class:`telegram.GameHighScore`]
Raises:
:exc:`TypeError` if :attr:`message` is not accessible.
@@ -833,6 +834,7 @@ class CallbackQuery(TelegramObject):
message_thread_id: Optional[int] = None,
reply_parameters: Optional["ReplyParameters"] = None,
show_caption_above_media: Optional[bool] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
@@ -880,6 +882,7 @@ class CallbackQuery(TelegramObject):
message_thread_id=message_thread_id,
reply_parameters=reply_parameters,
show_caption_above_media=show_caption_above_media,
allow_paid_broadcast=allow_paid_broadcast,
)
MAX_ANSWER_TEXT_LENGTH: Final[int] = (
+97 -13
View File
@@ -18,9 +18,10 @@
# 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 an object that represents a Telegram Chat."""
from collections.abc import Sequence
from datetime import datetime
from html import escape
from typing import TYPE_CHECKING, Final, Optional, Sequence, Tuple, Union
from typing import TYPE_CHECKING, Final, Optional, Union
from telegram import constants
from telegram._chatpermissions import ChatPermissions
@@ -43,6 +44,7 @@ if TYPE_CHECKING:
ChatMember,
Contact,
Document,
Gift,
InlineKeyboardMarkup,
InputMediaAudio,
InputMediaDocument,
@@ -296,7 +298,7 @@ class _ChatBase(TelegramObject):
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> Tuple["ChatMember", ...]:
) -> tuple["ChatMember", ...]:
"""Shortcut for::
await bot.get_chat_administrators(update.effective_chat.id, *args, **kwargs)
@@ -305,7 +307,7 @@ class _ChatBase(TelegramObject):
:meth:`telegram.Bot.get_chat_administrators`.
Returns:
Tuple[:class:`telegram.ChatMember`]: A tuple of administrators in a chat. An Array of
tuple[:class:`telegram.ChatMember`]: A tuple of administrators in a chat. An Array of
:class:`telegram.ChatMember` objects that contains information about all
chat administrators except other bots. If the chat is a group or a supergroup
and no administrators were appointed, only the creator will be returned.
@@ -1011,6 +1013,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1052,6 +1055,7 @@ class _ChatBase(TelegramObject):
api_kwargs=api_kwargs,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def delete_message(
@@ -1129,6 +1133,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1140,7 +1145,7 @@ class _ChatBase(TelegramObject):
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Optional[Sequence["MessageEntity"]] = None,
) -> Tuple["Message", ...]:
) -> tuple["Message", ...]:
"""Shortcut for::
await bot.send_media_group(update.effective_chat.id, *args, **kwargs)
@@ -1148,7 +1153,7 @@ class _ChatBase(TelegramObject):
For the documentation of the arguments, please see :meth:`telegram.Bot.send_media_group`.
Returns:
Tuple[:class:`telegram.Message`]: On success, a tuple of :class:`~telegram.Message`
tuple[:class:`telegram.Message`]: On success, a tuple of :class:`~telegram.Message`
instances that were sent is returned.
"""
@@ -1171,6 +1176,7 @@ class _ChatBase(TelegramObject):
reply_parameters=reply_parameters,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def send_chat_action(
@@ -1224,6 +1230,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
show_caption_above_media: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
@@ -1267,6 +1274,7 @@ class _ChatBase(TelegramObject):
has_spoiler=has_spoiler,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
show_caption_above_media=show_caption_above_media,
)
@@ -1283,6 +1291,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1324,6 +1333,7 @@ class _ChatBase(TelegramObject):
message_thread_id=message_thread_id,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def send_audio(
@@ -1343,6 +1353,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1388,6 +1399,7 @@ class _ChatBase(TelegramObject):
thumbnail=thumbnail,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def send_document(
@@ -1405,6 +1417,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1448,6 +1461,7 @@ class _ChatBase(TelegramObject):
message_thread_id=message_thread_id,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def send_dice(
@@ -1460,6 +1474,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1496,6 +1511,7 @@ class _ChatBase(TelegramObject):
message_thread_id=message_thread_id,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def send_game(
@@ -1508,6 +1524,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1544,6 +1561,7 @@ class _ChatBase(TelegramObject):
message_thread_id=message_thread_id,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def send_invoice(
@@ -1575,6 +1593,7 @@ class _ChatBase(TelegramObject):
message_thread_id: Optional[int] = None,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1640,6 +1659,7 @@ class _ChatBase(TelegramObject):
message_thread_id=message_thread_id,
reply_parameters=reply_parameters,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def send_location(
@@ -1657,6 +1677,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1700,6 +1721,7 @@ class _ChatBase(TelegramObject):
message_thread_id=message_thread_id,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def send_animation(
@@ -1720,6 +1742,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
show_caption_above_media: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
@@ -1767,6 +1790,7 @@ class _ChatBase(TelegramObject):
thumbnail=thumbnail,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
show_caption_above_media=show_caption_above_media,
)
@@ -1781,6 +1805,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1818,6 +1843,7 @@ class _ChatBase(TelegramObject):
emoji=emoji,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def send_venue(
@@ -1837,6 +1863,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1882,6 +1909,7 @@ class _ChatBase(TelegramObject):
message_thread_id=message_thread_id,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def send_video(
@@ -1903,6 +1931,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
show_caption_above_media: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
@@ -1951,6 +1980,7 @@ class _ChatBase(TelegramObject):
has_spoiler=has_spoiler,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
show_caption_above_media=show_caption_above_media,
)
@@ -1967,6 +1997,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2008,6 +2039,7 @@ class _ChatBase(TelegramObject):
message_thread_id=message_thread_id,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def send_voice(
@@ -2024,6 +2056,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
business_connection_id: Optional[str] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2066,6 +2099,7 @@ class _ChatBase(TelegramObject):
message_thread_id=message_thread_id,
business_connection_id=business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def send_poll(
@@ -2091,6 +2125,7 @@ class _ChatBase(TelegramObject):
question_parse_mode: ODVInput[str] = DEFAULT_NONE,
question_entities: Optional[Sequence["MessageEntity"]] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2128,6 +2163,7 @@ class _ChatBase(TelegramObject):
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
explanation=explanation,
explanation_parse_mode=explanation_parse_mode,
open_period=open_period,
@@ -2155,6 +2191,7 @@ class _ChatBase(TelegramObject):
message_thread_id: Optional[int] = None,
reply_parameters: Optional["ReplyParameters"] = None,
show_caption_above_media: Optional[bool] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2196,6 +2233,7 @@ class _ChatBase(TelegramObject):
protect_content=protect_content,
message_thread_id=message_thread_id,
show_caption_above_media=show_caption_above_media,
allow_paid_broadcast=allow_paid_broadcast,
)
async def copy_message(
@@ -2211,6 +2249,7 @@ class _ChatBase(TelegramObject):
message_thread_id: Optional[int] = None,
reply_parameters: Optional["ReplyParameters"] = None,
show_caption_above_media: Optional[bool] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2252,6 +2291,7 @@ class _ChatBase(TelegramObject):
protect_content=protect_content,
message_thread_id=message_thread_id,
show_caption_above_media=show_caption_above_media,
allow_paid_broadcast=allow_paid_broadcast,
)
async def send_copies(
@@ -2268,7 +2308,7 @@ class _ChatBase(TelegramObject):
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> Tuple["MessageId", ...]:
) -> tuple["MessageId", ...]:
"""Shortcut for::
await bot.copy_messages(chat_id=update.effective_chat.id, *args, **kwargs)
@@ -2280,7 +2320,7 @@ class _ChatBase(TelegramObject):
.. versionadded:: 20.8
Returns:
Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId`
tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId`
of the sent messages is returned.
"""
@@ -2313,7 +2353,7 @@ class _ChatBase(TelegramObject):
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> Tuple["MessageId", ...]:
) -> tuple["MessageId", ...]:
"""Shortcut for::
await bot.copy_messages(from_chat_id=update.effective_chat.id, *args, **kwargs)
@@ -2325,7 +2365,7 @@ class _ChatBase(TelegramObject):
.. versionadded:: 20.8
Returns:
Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId`
tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId`
of the sent messages is returned.
"""
@@ -2442,7 +2482,7 @@ class _ChatBase(TelegramObject):
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> Tuple["MessageId", ...]:
) -> tuple["MessageId", ...]:
"""Shortcut for::
await bot.forward_messages(chat_id=update.effective_chat.id, *args, **kwargs)
@@ -2454,7 +2494,7 @@ class _ChatBase(TelegramObject):
.. versionadded:: 20.8
Returns:
Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId`
tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId`
of sent messages is returned.
"""
@@ -2485,7 +2525,7 @@ class _ChatBase(TelegramObject):
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> Tuple["MessageId", ...]:
) -> tuple["MessageId", ...]:
"""Shortcut for::
await bot.forward_messages(from_chat_id=update.effective_chat.id, *args, **kwargs)
@@ -2497,7 +2537,7 @@ class _ChatBase(TelegramObject):
.. versionadded:: 20.8
Returns:
Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId`
tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId`
of sent messages is returned.
"""
@@ -3350,6 +3390,8 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
reply_markup: Optional[ReplyMarkup] = None,
business_connection_id: Optional[str] = None,
payload: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
@@ -3391,6 +3433,48 @@ class _ChatBase(TelegramObject):
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
business_connection_id=business_connection_id,
payload=payload,
allow_paid_broadcast=allow_paid_broadcast,
)
async def send_gift(
self,
gift_id: Union[str, "Gift"],
text: Optional[str] = None,
text_parse_mode: ODVInput[str] = DEFAULT_NONE,
text_entities: Optional[Sequence["MessageEntity"]] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> bool:
"""Shortcut for::
await bot.send_gift(user_id=update.effective_chat.id, *args, **kwargs )
For the documentation of the arguments, please see :meth:`telegram.Bot.send_gift`.
Caution:
Can only work, if the chat is a private chat, see :attr:`type`.
.. versionadded:: 21.8
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
"""
return await self.get_bot().send_gift(
user_id=self.id,
gift_id=gift_id,
text=text,
text_parse_mode=text_parse_mode,
text_entities=text_entities,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
)
+5 -4
View File
@@ -17,7 +17,8 @@
# 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 objects related to chat backgrounds."""
from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type
from collections.abc import Sequence
from typing import TYPE_CHECKING, Final, Optional
from telegram import constants
from telegram._files.document import Document
@@ -87,7 +88,7 @@ class BackgroundFill(TelegramObject):
if not data:
return None
_class_mapping: Dict[str, Type[BackgroundFill]] = {
_class_mapping: dict[str, type[BackgroundFill]] = {
cls.SOLID: BackgroundFillSolid,
cls.GRADIENT: BackgroundFillGradient,
cls.FREEFORM_GRADIENT: BackgroundFillFreeformGradient,
@@ -212,7 +213,7 @@ class BackgroundFillFreeformGradient(BackgroundFill):
super().__init__(type=self.FREEFORM_GRADIENT, api_kwargs=api_kwargs)
with self._unfrozen():
self.colors: Tuple[int, ...] = parse_sequence_arg(colors)
self.colors: tuple[int, ...] = parse_sequence_arg(colors)
self._id_attrs = (self.colors,)
@@ -278,7 +279,7 @@ class BackgroundType(TelegramObject):
if not data:
return None
_class_mapping: Dict[str, Type[BackgroundType]] = {
_class_mapping: dict[str, type[BackgroundType]] = {
cls.FILL: BackgroundTypeFill,
cls.WALLPAPER: BackgroundTypeWallpaper,
cls.PATTERN: BackgroundTypePattern,
+22 -8
View File
@@ -18,8 +18,9 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram ChatBoosts."""
from collections.abc import Sequence
from datetime import datetime
from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type
from typing import TYPE_CHECKING, Final, Optional
from telegram import constants
from telegram._chat import Chat
@@ -119,7 +120,7 @@ class ChatBoostSource(TelegramObject):
if not data:
return None
_class_mapping: Dict[str, Type[ChatBoostSource]] = {
_class_mapping: dict[str, type[ChatBoostSource]] = {
cls.PREMIUM: ChatBoostSourcePremium,
cls.GIFT_CODE: ChatBoostSourceGiftCode,
cls.GIVEAWAY: ChatBoostSourceGiveaway,
@@ -187,15 +188,22 @@ class ChatBoostSourceGiftCode(ChatBoostSource):
class ChatBoostSourceGiveaway(ChatBoostSource):
"""
The boost was obtained by the creation of a Telegram Premium giveaway. This boosts the chat 4
times for the duration of the corresponding Telegram Premium subscription.
The boost was obtained by the creation of a Telegram Premium giveaway or a Telegram Star.
This boosts the chat 4 times for the duration of the corresponding Telegram Premium
subscription for Telegram Premium giveaways and :attr:`prize_star_count` / 500 times for
one year for Telegram Star giveaways.
.. versionadded:: 20.8
Args:
giveaway_message_id (:obj:`int`): Identifier of a message in the chat with the giveaway;
the message could have been deleted already. May be 0 if the message isn't sent yet.
user (:class:`telegram.User`, optional): User that won the prize in the giveaway if any.
user (:class:`telegram.User`, optional): User that won the prize in the giveaway if any;
for Telegram Premium giveaways only.
prize_star_count (:obj:`int`, optional): The number of Telegram Stars to be split between
giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
is_unclaimed (:obj:`bool`, optional): :obj:`True`, if the giveaway was completed, but
there was no user to win the prize.
@@ -205,17 +213,22 @@ class ChatBoostSourceGiveaway(ChatBoostSource):
giveaway_message_id (:obj:`int`): Identifier of a message in the chat with the giveaway;
the message could have been deleted already. May be 0 if the message isn't sent yet.
user (:class:`telegram.User`): Optional. User that won the prize in the giveaway if any.
prize_star_count (:obj:`int`): Optional. The number of Telegram Stars to be split between
giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
is_unclaimed (:obj:`bool`): Optional. :obj:`True`, if the giveaway was completed, but
there was no user to win the prize.
"""
__slots__ = ("giveaway_message_id", "is_unclaimed", "user")
__slots__ = ("giveaway_message_id", "is_unclaimed", "prize_star_count", "user")
def __init__(
self,
giveaway_message_id: int,
user: Optional[User] = None,
is_unclaimed: Optional[bool] = None,
prize_star_count: Optional[int] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
@@ -224,6 +237,7 @@ class ChatBoostSourceGiveaway(ChatBoostSource):
with self._unfrozen():
self.giveaway_message_id: int = giveaway_message_id
self.user: Optional[User] = user
self.prize_star_count: Optional[int] = prize_star_count
self.is_unclaimed: Optional[bool] = is_unclaimed
@@ -418,7 +432,7 @@ class UserChatBoosts(TelegramObject):
user.
Attributes:
boosts (Tuple[:class:`telegram.ChatBoost`]): List of boosts added to the chat by the user.
boosts (tuple[:class:`telegram.ChatBoost`]): List of boosts added to the chat by the user.
"""
__slots__ = ("boosts",)
@@ -431,7 +445,7 @@ class UserChatBoosts(TelegramObject):
):
super().__init__(api_kwargs=api_kwargs)
self.boosts: Tuple[ChatBoost, ...] = parse_sequence_arg(boosts)
self.boosts: tuple[ChatBoost, ...] = parse_sequence_arg(boosts)
self._id_attrs = (self.boosts,)
self._freeze()
+6 -5
View File
@@ -18,8 +18,9 @@
# 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 an object that represents a Telegram ChatFullInfo."""
from collections.abc import Sequence
from datetime import datetime
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from typing import TYPE_CHECKING, Optional
from telegram._birthdate import Birthdate
from telegram._chat import Chat, _ChatBase
@@ -224,7 +225,7 @@ class ChatFullInfo(_ChatBase):
.. versionadded:: 20.0
photo (:class:`telegram.ChatPhoto`): Optional. Chat photo.
active_usernames (Tuple[:obj:`str`]): Optional. If set, the list of all `active chat
active_usernames (tuple[:obj:`str`]): Optional. If set, the list of all `active chat
usernames <https://telegram.org/blog/topics-in-groups-collectible-usernames\
#collectible-usernames>`_; for private chats, supergroups and channels.
@@ -252,7 +253,7 @@ class ChatFullInfo(_ChatBase):
of the user.
.. versionadded:: 21.1
available_reactions (Tuple[:class:`telegram.ReactionType`]): Optional. List of available
available_reactions (tuple[:class:`telegram.ReactionType`]): Optional. List of available
reactions allowed in the chat. If omitted, then all of
:const:`telegram.constants.ReactionEmoji` are allowed.
@@ -483,14 +484,14 @@ class ChatFullInfo(_ChatBase):
self.has_restricted_voice_and_video_messages: Optional[bool] = (
has_restricted_voice_and_video_messages
)
self.active_usernames: Tuple[str, ...] = parse_sequence_arg(active_usernames)
self.active_usernames: tuple[str, ...] = parse_sequence_arg(active_usernames)
self.emoji_status_custom_emoji_id: Optional[str] = emoji_status_custom_emoji_id
self.emoji_status_expiration_date: Optional[datetime] = emoji_status_expiration_date
self.has_aggressive_anti_spam_enabled: Optional[bool] = (
has_aggressive_anti_spam_enabled
)
self.has_hidden_members: Optional[bool] = has_hidden_members
self.available_reactions: Optional[Tuple[ReactionType, ...]] = parse_sequence_arg(
self.available_reactions: Optional[tuple[ReactionType, ...]] = parse_sequence_arg(
available_reactions
)
self.accent_color_id: Optional[int] = accent_color_id
+2 -2
View File
@@ -19,7 +19,7 @@
"""This module contains an object that represents a Telegram ChatMember."""
import datetime
from typing import TYPE_CHECKING, Dict, Final, Optional, Type
from typing import TYPE_CHECKING, Final, Optional
from telegram import constants
from telegram._telegramobject import TelegramObject
@@ -114,7 +114,7 @@ class ChatMember(TelegramObject):
if not data:
return None
_class_mapping: Dict[str, Type[ChatMember]] = {
_class_mapping: dict[str, type[ChatMember]] = {
cls.OWNER: ChatMemberOwner,
cls.ADMINISTRATOR: ChatMemberAdministrator,
cls.MEMBER: ChatMemberMember,
+5 -5
View File
@@ -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 ChatMemberUpdated."""
import datetime
from typing import TYPE_CHECKING, Dict, Optional, Tuple, Union
from typing import TYPE_CHECKING, Optional, Union
from telegram._chat import Chat
from telegram._chatinvitelink import ChatInviteLink
@@ -162,7 +162,7 @@ class ChatMemberUpdated(TelegramObject):
return super().de_json(data=data, bot=bot)
def _get_attribute_difference(self, attribute: str) -> Tuple[object, object]:
def _get_attribute_difference(self, attribute: str) -> tuple[object, object]:
try:
old = self.old_chat_member[attribute]
except KeyError:
@@ -177,9 +177,9 @@ class ChatMemberUpdated(TelegramObject):
def difference(
self,
) -> Dict[
) -> dict[
str,
Tuple[
tuple[
Union[str, bool, datetime.datetime, User], Union[str, bool, datetime.datetime, User]
],
]:
@@ -198,7 +198,7 @@ class ChatMemberUpdated(TelegramObject):
.. versionadded:: 13.5
Returns:
Dict[:obj:`str`, Tuple[:class:`object`, :class:`object`]]: A dictionary mapping
dict[:obj:`str`, tuple[:class:`object`, :class:`object`]]: A dictionary mapping
attribute names to tuples of the form ``(old_value, new_value)``
"""
# we first get the names of the attributes that have changed
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2024
# 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 an object that represents a Telegram CopyTextButton."""
from typing import Optional
from telegram._telegramobject import TelegramObject
from telegram._utils.types import JSONDict
class CopyTextButton(TelegramObject):
"""
This object represents an inline keyboard button that copies specified text to the clipboard.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`text` is equal.
.. versionadded:: 21.7
Args:
text (:obj:`str`): The text to be copied to the clipboard;
:tg-const:`telegram.constants.InlineKeyboardButtonLimit.MIN_COPY_TEXT`-
:tg-const:`telegram.constants.InlineKeyboardButtonLimit.MAX_COPY_TEXT` characters
Attributes:
text (:obj:`str`): The text to be copied to the clipboard;
:tg-const:`telegram.constants.InlineKeyboardButtonLimit.MIN_COPY_TEXT`-
:tg-const:`telegram.constants.InlineKeyboardButtonLimit.MAX_COPY_TEXT` characters
"""
__slots__ = ("text",)
def __init__(self, text: str, *, api_kwargs: Optional[JSONDict] = None):
super().__init__(api_kwargs=api_kwargs)
self.text: str = text
self._id_attrs = (self.text,)
self._freeze()
+3 -3
View File
@@ -17,7 +17,7 @@
# 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 an object that represents a Telegram Dice."""
from typing import Final, List, Optional
from typing import Final, Optional
from telegram import constants
from telegram._telegramobject import TelegramObject
@@ -114,8 +114,8 @@ class Dice(TelegramObject):
.. versionadded:: 13.4
"""
ALL_EMOJI: Final[List[str]] = list(constants.DiceEmoji)
"""List[:obj:`str`]: A list of all available dice emoji."""
ALL_EMOJI: Final[list[str]] = list(constants.DiceEmoji)
"""list[:obj:`str`]: A list of all available dice emoji."""
MIN_VALUE: Final[int] = constants.DiceLimit.MIN_VALUE
""":const:`telegram.constants.DiceLimit.MIN_VALUE`
+2 -2
View File
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Common base class for media objects with thumbnails"""
from typing import TYPE_CHECKING, Optional, Type, TypeVar
from typing import TYPE_CHECKING, Optional, TypeVar
from telegram._files._basemedium import _BaseMedium
from telegram._files.photosize import PhotoSize
@@ -82,7 +82,7 @@ class _BaseThumbedMedium(_BaseMedium):
@classmethod
def de_json(
cls: Type[ThumbedMT_co], data: Optional[JSONDict], bot: Optional["Bot"] = None
cls: type[ThumbedMT_co], data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional[ThumbedMT_co]:
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
+36 -6
View File
@@ -128,9 +128,8 @@ class File(TelegramObject):
) -> Path:
"""
Download this file. By default, the file is saved in the current working directory with
:attr:`file_path` as file name. If the file has no filename, the file ID will be used as
filename. If :paramref:`custom_path` is supplied as a :obj:`str` or :obj:`pathlib.Path`,
it will be saved to that path.
:attr:`file_path` as file name. If :paramref:`custom_path` is supplied as a :obj:`str` or
:obj:`pathlib.Path`, it will be saved to that path.
Note:
If :paramref:`custom_path` isn't provided and :attr:`file_path` is the path of a
@@ -152,6 +151,11 @@ class File(TelegramObject):
* This method was previously called ``download``. It was split into
:meth:`download_to_drive` and :meth:`download_to_memory`.
.. versionchanged:: 21.7
Raises :exc:`RuntimeError` if :attr:`file_path` is not set. Note that files without
a :attr:`file_path` could never be downloaded, as this attribute is mandatory for that
operation.
Args:
custom_path (:class:`pathlib.Path` | :obj:`str` , optional): The path where the file
will be saved to. If not specified, will be saved in the current working directory
@@ -175,7 +179,13 @@ class File(TelegramObject):
Returns:
:class:`pathlib.Path`: Returns the Path object the file was downloaded to.
Raises:
RuntimeError: If :attr:`file_path` is not set.
"""
if not self.file_path:
raise RuntimeError("No `file_path` available for this file. Can not download.")
local_file = is_local_file(self.file_path)
url = None if local_file else self._get_encoded_url()
@@ -198,10 +208,8 @@ class File(TelegramObject):
filename = Path(custom_path)
elif local_file:
return Path(self.file_path)
elif self.file_path:
filename = Path(Path(self.file_path).name)
else:
filename = Path.cwd() / self.file_id
filename = Path(Path(self.file_path).name)
buf = await self.get_bot().request.retrieve(
url,
@@ -237,6 +245,11 @@ class File(TelegramObject):
.. versionadded:: 20.0
.. versionchanged:: 21.7
Raises :exc:`RuntimeError` if :attr:`file_path` is not set. Note that files without
a :attr:`file_path` could never be downloaded, as this attribute is mandatory for that
operation.
Args:
out (:obj:`io.BufferedIOBase`): A file-like object. Must be opened for writing in
binary mode.
@@ -254,7 +267,13 @@ class File(TelegramObject):
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`.
Raises:
RuntimeError: If :attr:`file_path` is not set.
"""
if not self.file_path:
raise RuntimeError("No `file_path` available for this file. Can not download.")
local_file = is_local_file(self.file_path)
url = None if local_file else self._get_encoded_url()
path = Path(self.file_path) if local_file else None
@@ -283,6 +302,11 @@ class File(TelegramObject):
) -> bytearray:
"""Download this file and return it as a bytearray.
.. versionchanged:: 21.7
Raises :exc:`RuntimeError` if :attr:`file_path` is not set. Note that files without
a :attr:`file_path` could never be downloaded, as this attribute is mandatory for that
operation.
Args:
buf (:obj:`bytearray`, optional): Extend the given bytearray with the downloaded data.
@@ -312,7 +336,13 @@ class File(TelegramObject):
:obj:`bytearray`: The same object as :paramref:`buf` if it was specified. Otherwise a
newly allocated :obj:`bytearray`.
Raises:
RuntimeError: If :attr:`file_path` is not set.
"""
if not self.file_path:
raise RuntimeError("No `file_path` available for this file. Can not download.")
if buf is None:
buf = bytearray()
+3 -3
View File
@@ -56,8 +56,8 @@ class InputFile:
read_file_handle (:obj:`bool`, optional): If :obj:`True` and :paramref:`obj` is a file
handle, the data will be read from the file handle on initialization of this object.
If :obj:`False`, the file handle will be passed on to the
`networking backend <telegram.request.BaseRequest.do_request>`_ which will have to
handle the reading. Defaults to :obj:`True`.
:attr:`networking backend <telegram.request.BaseRequest.do_request>` which will have
to handle the reading. Defaults to :obj:`True`.
Tip:
If you upload extremely large files, you may want to set this to :obj:`False` to
@@ -130,7 +130,7 @@ class InputFile:
Content may now be a file handle.
Returns:
Tuple[:obj:`str`, :obj:`bytes` | :class:`IO`, :obj:`str`]:
tuple[:obj:`str`, :obj:`bytes` | :class:`IO`, :obj:`str`]:
"""
return self.filename, self.input_file_content, self.mimetype
+9 -8
View File
@@ -17,7 +17,8 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Base class for Telegram InputMedia Objects."""
from typing import Final, Optional, Sequence, Tuple, Union
from collections.abc import Sequence
from typing import Final, Optional, Union
from telegram import constants
from telegram._files.animation import Animation
@@ -74,7 +75,7 @@ class InputMedia(TelegramObject):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities
parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -99,7 +100,7 @@ class InputMedia(TelegramObject):
self.type: str = enum.get_member(constants.InputMediaType, media_type, media_type)
self.media: Union[str, InputFile, Animation, Audio, Document, PhotoSize, Video] = media
self.caption: Optional[str] = caption
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.parse_mode: ODVInput[str] = parse_mode
self._freeze()
@@ -321,7 +322,7 @@ class InputMediaAnimation(InputMedia):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting.
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -436,7 +437,7 @@ class InputMediaPhoto(InputMedia):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -546,7 +547,7 @@ class InputMediaVideo(InputMedia):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -676,7 +677,7 @@ class InputMediaAudio(InputMedia):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -779,7 +780,7 @@ class InputMediaDocument(InputMedia):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
+6 -5
View File
@@ -18,7 +18,8 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram InputSticker."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple, Union
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional, Union
from telegram._files.sticker import MaskPosition
from telegram._telegramobject import TelegramObject
@@ -67,13 +68,13 @@ class InputSticker(TelegramObject):
Attributes:
sticker (:obj:`str` | :class:`telegram.InputFile`): The added sticker.
emoji_list (Tuple[:obj:`str`]): Tuple of
emoji_list (tuple[:obj:`str`]): Tuple of
:tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI` -
:tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with the
sticker.
mask_position (:class:`telegram.MaskPosition`): Optional. Position where the mask should be
placed on faces. For ":tg-const:`telegram.constants.StickerType.MASK`" stickers only.
keywords (Tuple[:obj:`str`]): Optional. Tuple of
keywords (tuple[:obj:`str`]): Optional. Tuple of
0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords
for the sticker with the total length of up to
:tg-const:`telegram.constants.StickerLimit.MAX_KEYWORD_LENGTH` characters. For
@@ -110,9 +111,9 @@ class InputSticker(TelegramObject):
local_mode=True,
attach=True,
)
self.emoji_list: Tuple[str, ...] = parse_sequence_arg(emoji_list)
self.emoji_list: tuple[str, ...] = parse_sequence_arg(emoji_list)
self.format: str = format
self.mask_position: Optional[MaskPosition] = mask_position
self.keywords: Tuple[str, ...] = parse_sequence_arg(keywords)
self.keywords: tuple[str, ...] = parse_sequence_arg(keywords)
self._freeze()
+4 -3
View File
@@ -17,7 +17,8 @@
# 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 objects that represent stickers."""
from typing import TYPE_CHECKING, Final, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Final, Optional
from telegram import constants
from telegram._files._basethumbedmedium import _BaseThumbedMedium
@@ -259,7 +260,7 @@ class StickerSet(TelegramObject):
Attributes:
name (:obj:`str`): Sticker set name.
title (:obj:`str`): Sticker set title.
stickers (Tuple[:class:`telegram.Sticker`]): List of all set stickers.
stickers (tuple[:class:`telegram.Sticker`]): List of all set stickers.
.. versionchanged:: 20.0
|tupleclassattrs|
@@ -296,7 +297,7 @@ class StickerSet(TelegramObject):
super().__init__(api_kwargs=api_kwargs)
self.name: str = name
self.title: str = title
self.stickers: Tuple[Sticker, ...] = parse_sequence_arg(stickers)
self.stickers: tuple[Sticker, ...] = parse_sequence_arg(stickers)
self.sticker_type: str = sticker_type
# Optional
self.thumbnail: Optional[PhotoSize] = thumbnail
+9 -8
View File
@@ -17,7 +17,8 @@
# 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 an object that represents a Telegram Game."""
from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._files.animation import Animation
from telegram._files.photosize import PhotoSize
@@ -65,7 +66,7 @@ class Game(TelegramObject):
Attributes:
title (:obj:`str`): Title of the game.
description (:obj:`str`): Description of the game.
photo (Tuple[:class:`telegram.PhotoSize`]): Photo that will be displayed in the game
photo (tuple[:class:`telegram.PhotoSize`]): Photo that will be displayed in the game
message in chats.
.. versionchanged:: 20.0
@@ -76,7 +77,7 @@ class Game(TelegramObject):
when the bot calls :meth:`telegram.Bot.set_game_score`, or manually edited
using :meth:`telegram.Bot.edit_message_text`.
0-:tg-const:`telegram.constants.MessageLimit.MAX_TEXT_LENGTH` characters.
text_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. Special entities that
text_entities (tuple[:class:`telegram.MessageEntity`]): Optional. Special entities that
appear in text, such as usernames, URLs, bot commands, etc.
This tuple is empty if the message does not contain text entities.
@@ -112,10 +113,10 @@ class Game(TelegramObject):
# Required
self.title: str = title
self.description: str = description
self.photo: Tuple[PhotoSize, ...] = parse_sequence_arg(photo)
self.photo: tuple[PhotoSize, ...] = parse_sequence_arg(photo)
# Optionals
self.text: Optional[str] = text
self.text_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(text_entities)
self.text_entities: tuple[MessageEntity, ...] = parse_sequence_arg(text_entities)
self.animation: Optional[Animation] = animation
self._id_attrs = (self.title, self.description, self.photo)
@@ -163,7 +164,7 @@ class Game(TelegramObject):
return entity_text.decode(TextEncoding.UTF_16_LE)
def parse_text_entities(self, types: Optional[List[str]] = None) -> Dict[MessageEntity, str]:
def parse_text_entities(self, types: Optional[list[str]] = None) -> dict[MessageEntity, str]:
"""
Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`.
It contains entities from this message filtered by their
@@ -176,13 +177,13 @@ class Game(TelegramObject):
See :attr:`parse_text_entity` for more info.
Args:
types (List[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as
types (list[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as
strings. If the :attr:`~telegram.MessageEntity.type` attribute of an entity is
contained in this list, it will be returned. Defaults to
:attr:`telegram.MessageEntity.ALL_TYPES`.
Returns:
Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to
dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to
the text that belongs to them, calculated based on UTF-16 codepoints.
"""
+136
View File
@@ -0,0 +1,136 @@
#!/usr/bin/env python
# pylint: disable=redefined-builtin
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2024
# 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 classes related to gifs sent by bots."""
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._files.sticker import Sticker
from telegram._telegramobject import TelegramObject
from telegram._utils.argumentparsing import parse_sequence_arg
from telegram._utils.types import JSONDict
if TYPE_CHECKING:
from telegram import Bot
class Gift(TelegramObject):
"""This object represents a gift that can be sent by the bot.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal if their :attr:`id` is equal.
.. versionadded:: 21.8
Args:
id (:obj:`str`): Unique identifier of the gift
sticker (:class:`~telegram.Sticker`): The sticker that represents the gift
star_count (:obj:`int`): The number of Telegram Stars that must be paid to send the sticker
total_count (:obj:`int`, optional): The total number of the gifts of this type that can be
sent; for limited gifts only
remaining_count (:obj:`int`, optional): The number of remaining gifts of this type that can
be sent; for limited gifts only
Attributes:
id (:obj:`str`): Unique identifier of the gift
sticker (:class:`~telegram.Sticker`): The sticker that represents the gift
star_count (:obj:`int`): The number of Telegram Stars that must be paid to send the sticker
total_count (:obj:`int`): Optional. The total number of the gifts of this type that can be
sent; for limited gifts only
remaining_count (:obj:`int`): Optional. The number of remaining gifts of this type that can
be sent; for limited gifts only
"""
__slots__ = ("id", "remaining_count", "star_count", "sticker", "total_count")
def __init__(
self,
id: str,
sticker: Sticker,
star_count: int,
total_count: Optional[int] = None,
remaining_count: Optional[int] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.id: str = id
self.sticker: Sticker = sticker
self.star_count: int = star_count
self.total_count: Optional[int] = total_count
self.remaining_count: Optional[int] = remaining_count
self._id_attrs = (self.id,)
self._freeze()
@classmethod
def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["Gift"]:
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
if not data:
return None
data["sticker"] = Sticker.de_json(data.get("sticker"), bot)
return cls(**data)
class Gifts(TelegramObject):
"""This object represent a list of gifts.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal if their :attr:`gifts` are equal.
.. versionadded:: 21.8
Args:
gifts (Sequence[:class:`Gift`]): The sequence of gifts
Attributes:
gifts (tuple[:class:`Gift`]): The sequence of gifts
"""
__slots__ = ("gifts",)
def __init__(
self,
gifts: Sequence[Gift],
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.gifts: tuple[Gift, ...] = parse_sequence_arg(gifts)
self._id_attrs = (self.gifts,)
self._freeze()
@classmethod
def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["Gifts"]:
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
if not data:
return None
data["gifts"] = Gift.de_list(data.get("gifts"), bot)
return cls(**data)
+62 -13
View File
@@ -18,7 +18,8 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an objects that are related to Telegram giveaways."""
import datetime
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._chat import Chat
from telegram._telegramobject import TelegramObject
@@ -41,7 +42,7 @@ class Giveaway(TelegramObject):
.. versionadded:: 20.8
Args:
chats (Tuple[:class:`telegram.Chat`]): The list of chats which the user must join to
chats (tuple[:class:`telegram.Chat`]): The list of chats which the user must join to
participate in the giveaway.
winners_selection_date (:class:`datetime.datetime`): The date when the giveaway winner will
be selected. |datetime_localization|
@@ -56,8 +57,13 @@ class Giveaway(TelegramObject):
country codes indicating the countries from which eligible users for the giveaway must
come. If empty, then all users can participate in the giveaway. Users with a phone
number that was bought on Fragment can always participate in giveaways.
prize_star_count (:obj:`int`, optional): The number of Telegram Stars to be split between
giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
premium_subscription_month_count (:obj:`int`, optional): The number of months the Telegram
Premium subscription won from the giveaway will be active for.
Premium subscription won from the giveaway will be active for; for Telegram Premium
giveaways only.
Attributes:
chats (Sequence[:class:`telegram.Chat`]): The list of chats which the user must join to
@@ -71,12 +77,17 @@ class Giveaway(TelegramObject):
has_public_winners (:obj:`True`): Optional. :obj:`True`, if the list of giveaway winners
will be visible to everyone
prize_description (:obj:`str`): Optional. Description of additional giveaway prize
country_codes (Tuple[:obj:`str`]): Optional. A tuple of two-letter ISO 3166-1 alpha-2
country_codes (tuple[:obj:`str`]): Optional. A tuple of two-letter ISO 3166-1 alpha-2
country codes indicating the countries from which eligible users for the giveaway must
come. If empty, then all users can participate in the giveaway. Users with a phone
number that was bought on Fragment can always participate in giveaways.
prize_star_count (:obj:`int`): Optional. The number of Telegram Stars to be split between
giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
premium_subscription_month_count (:obj:`int`): Optional. The number of months the Telegram
Premium subscription won from the giveaway will be active for.
Premium subscription won from the giveaway will be active for; for Telegram Premium
giveaways only.
"""
__slots__ = (
@@ -86,6 +97,7 @@ class Giveaway(TelegramObject):
"only_new_members",
"premium_subscription_month_count",
"prize_description",
"prize_star_count",
"winner_count",
"winners_selection_date",
)
@@ -100,19 +112,21 @@ class Giveaway(TelegramObject):
prize_description: Optional[str] = None,
country_codes: Optional[Sequence[str]] = None,
premium_subscription_month_count: Optional[int] = None,
prize_star_count: Optional[int] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.chats: Tuple[Chat, ...] = tuple(chats)
self.chats: tuple[Chat, ...] = tuple(chats)
self.winners_selection_date: datetime.datetime = winners_selection_date
self.winner_count: int = winner_count
self.only_new_members: Optional[bool] = only_new_members
self.has_public_winners: Optional[bool] = has_public_winners
self.prize_description: Optional[str] = prize_description
self.country_codes: Tuple[str, ...] = parse_sequence_arg(country_codes)
self.country_codes: tuple[str, ...] = parse_sequence_arg(country_codes)
self.premium_subscription_month_count: Optional[int] = premium_subscription_month_count
self.prize_star_count: Optional[int] = prize_star_count
self._id_attrs = (
self.chats,
@@ -145,13 +159,28 @@ class Giveaway(TelegramObject):
class GiveawayCreated(TelegramObject):
"""This object represents a service message about the creation of a scheduled giveaway.
Currently holds no information.
Args:
prize_star_count (:obj:`int`, optional): The number of Telegram Stars to be
split between giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
Attributes:
prize_star_count (:obj:`int`): Optional. The number of Telegram Stars to be
split between giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
"""
__slots__ = ()
__slots__ = ("prize_star_count",)
def __init__(self, *, api_kwargs: Optional[JSONDict] = None):
def __init__(
self, prize_star_count: Optional[int] = None, *, api_kwargs: Optional[JSONDict] = None
):
super().__init__(api_kwargs=api_kwargs)
self.prize_star_count: Optional[int] = prize_star_count
self._freeze()
@@ -173,6 +202,10 @@ class GiveawayWinners(TelegramObject):
winner_count (:obj:`int`): Total number of winners in the giveaway
winners (Sequence[:class:`telegram.User`]): List of up to
:tg-const:`telegram.constants.GiveawayLimit.MAX_WINNERS` winners of the giveaway
prize_star_count (:obj:`int`, optional): The number of Telegram Stars to be split between
giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
additional_chat_count (:obj:`int`, optional): The number of other chats the user had to
join in order to be eligible for the giveaway
premium_subscription_month_count (:obj:`int`, optional): The number of months the Telegram
@@ -190,10 +223,14 @@ class GiveawayWinners(TelegramObject):
winners_selection_date (:class:`datetime.datetime`): Point in time when winners of the
giveaway were selected. |datetime_localization|
winner_count (:obj:`int`): Total number of winners in the giveaway
winners (Tuple[:class:`telegram.User`]): tuple of up to
winners (tuple[:class:`telegram.User`]): tuple of up to
:tg-const:`telegram.constants.GiveawayLimit.MAX_WINNERS` winners of the giveaway
additional_chat_count (:obj:`int`): Optional. The number of other chats the user had to
join in order to be eligible for the giveaway
prize_star_count (:obj:`int`): Optional. The number of Telegram Stars to be split between
giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
premium_subscription_month_count (:obj:`int`): Optional. The number of months the Telegram
Premium subscription won from the giveaway will be active for
unclaimed_prize_count (:obj:`int`): Optional. Number of undistributed prizes
@@ -211,6 +248,7 @@ class GiveawayWinners(TelegramObject):
"only_new_members",
"premium_subscription_month_count",
"prize_description",
"prize_star_count",
"unclaimed_prize_count",
"was_refunded",
"winner_count",
@@ -231,6 +269,7 @@ class GiveawayWinners(TelegramObject):
only_new_members: Optional[bool] = None,
was_refunded: Optional[bool] = None,
prize_description: Optional[str] = None,
prize_star_count: Optional[int] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
@@ -240,13 +279,14 @@ class GiveawayWinners(TelegramObject):
self.giveaway_message_id: int = giveaway_message_id
self.winners_selection_date: datetime.datetime = winners_selection_date
self.winner_count: int = winner_count
self.winners: Tuple[User, ...] = tuple(winners)
self.winners: tuple[User, ...] = tuple(winners)
self.additional_chat_count: Optional[int] = additional_chat_count
self.premium_subscription_month_count: Optional[int] = premium_subscription_month_count
self.unclaimed_prize_count: Optional[int] = unclaimed_prize_count
self.only_new_members: Optional[bool] = only_new_members
self.was_refunded: Optional[bool] = was_refunded
self.prize_description: Optional[str] = prize_description
self.prize_star_count: Optional[int] = prize_star_count
self._id_attrs = (
self.chat,
@@ -295,21 +335,29 @@ class GiveawayCompleted(TelegramObject):
unclaimed_prize_count (:obj:`int`, optional): Number of undistributed prizes
giveaway_message (:class:`telegram.Message`, optional): Message with the giveaway that was
completed, if it wasn't deleted
is_star_giveaway (:obj:`bool`, optional): :obj:`True`, if the giveaway is a Telegram Star
giveaway. Otherwise, currently, the giveaway is a Telegram Premium giveaway.
.. versionadded:: 21.6
Attributes:
winner_count (:obj:`int`): Number of winners in the giveaway
unclaimed_prize_count (:obj:`int`): Optional. Number of undistributed prizes
giveaway_message (:class:`telegram.Message`): Optional. Message with the giveaway that was
completed, if it wasn't deleted
is_star_giveaway (:obj:`bool`): Optional. :obj:`True`, if the giveaway is a Telegram Star
giveaway. Otherwise, currently, the giveaway is a Telegram Premium giveaway.
.. versionadded:: 21.6
"""
__slots__ = ("giveaway_message", "unclaimed_prize_count", "winner_count")
__slots__ = ("giveaway_message", "is_star_giveaway", "unclaimed_prize_count", "winner_count")
def __init__(
self,
winner_count: int,
unclaimed_prize_count: Optional[int] = None,
giveaway_message: Optional["Message"] = None,
is_star_giveaway: Optional[bool] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
@@ -318,6 +366,7 @@ class GiveawayCompleted(TelegramObject):
self.winner_count: int = winner_count
self.unclaimed_prize_count: Optional[int] = unclaimed_prize_count
self.giveaway_message: Optional[Message] = giveaway_message
self.is_star_giveaway: Optional[bool] = is_star_giveaway
self._id_attrs = (
self.winner_count,
+13
View File
@@ -21,6 +21,7 @@
from typing import TYPE_CHECKING, Final, Optional, Union
from telegram import constants
from telegram._copytextbutton import CopyTextButton
from telegram._games.callbackgame import CallbackGame
from telegram._loginurl import LoginUrl
from telegram._switchinlinequerychosenchat import SwitchInlineQueryChosenChat
@@ -123,6 +124,10 @@ class InlineKeyboardButton(TelegramObject):
This offers a quick way for the user to open your bot in inline mode in the same chat
- good for selecting something from multiple options. Not supported in channels and for
messages sent on behalf of a Telegram Business account.
copy_text (:class:`telegram.CopyTextButton`, optional): Description of the button that
copies the specified text to the clipboard.
.. versionadded:: 21.7
callback_game (:class:`telegram.CallbackGame`, optional): Description of the game that will
be launched when the user presses the button
@@ -192,6 +197,10 @@ class InlineKeyboardButton(TelegramObject):
This offers a quick way for the user to open your bot in inline mode in the same chat
- good for selecting something from multiple options. Not supported in channels and for
messages sent on behalf of a Telegram Business account.
copy_text (:class:`telegram.CopyTextButton`): Optional. Description of the button that
copies the specified text to the clipboard.
.. versionadded:: 21.7
callback_game (:class:`telegram.CallbackGame`): Optional. Description of the game that will
be launched when the user presses the button.
@@ -224,6 +233,7 @@ class InlineKeyboardButton(TelegramObject):
__slots__ = (
"callback_data",
"callback_game",
"copy_text",
"login_url",
"pay",
"switch_inline_query",
@@ -246,6 +256,7 @@ class InlineKeyboardButton(TelegramObject):
login_url: Optional[LoginUrl] = None,
web_app: Optional[WebAppInfo] = None,
switch_inline_query_chosen_chat: Optional[SwitchInlineQueryChosenChat] = None,
copy_text: Optional[CopyTextButton] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
@@ -265,6 +276,7 @@ class InlineKeyboardButton(TelegramObject):
self.switch_inline_query_chosen_chat: Optional[SwitchInlineQueryChosenChat] = (
switch_inline_query_chosen_chat
)
self.copy_text: Optional[CopyTextButton] = copy_text
self._id_attrs = ()
self._set_id_attrs()
@@ -299,6 +311,7 @@ class InlineKeyboardButton(TelegramObject):
data["switch_inline_query_chosen_chat"] = SwitchInlineQueryChosenChat.de_json(
data.get("switch_inline_query_chosen_chat"), bot
)
data["copy_text"] = CopyTextButton.de_json(data.get("copy_text"), bot)
return super().de_json(data=data, bot=bot)
+4 -3
View File
@@ -17,7 +17,8 @@
# 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 an object that represents a Telegram InlineKeyboardMarkup."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardbutton import InlineKeyboardButton
from telegram._telegramobject import TelegramObject
@@ -57,7 +58,7 @@ class InlineKeyboardMarkup(TelegramObject):
|sequenceclassargs|
Attributes:
inline_keyboard (Tuple[Tuple[:class:`telegram.InlineKeyboardButton`]]): Tuple of
inline_keyboard (tuple[tuple[:class:`telegram.InlineKeyboardButton`]]): Tuple of
button rows, each represented by a tuple of :class:`~telegram.InlineKeyboardButton`
objects.
@@ -81,7 +82,7 @@ class InlineKeyboardMarkup(TelegramObject):
"InlineKeyboardButtons"
)
# Required
self.inline_keyboard: Tuple[Tuple[InlineKeyboardButton, ...], ...] = tuple(
self.inline_keyboard: tuple[tuple[InlineKeyboardButton, ...], ...] = tuple(
tuple(row) for row in inline_keyboard
)
+2 -1
View File
@@ -19,7 +19,8 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram InlineQuery."""
from typing import TYPE_CHECKING, Callable, Final, Optional, Sequence, Union
from collections.abc import Sequence
from typing import TYPE_CHECKING, Callable, Final, Optional, Union
from telegram import constants
from telegram._files.location import Location
+4 -3
View File
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InlineQueryResultAudio."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from telegram._inline.inlinequeryresult import InlineQueryResult
@@ -73,7 +74,7 @@ class InlineQueryResultAudio(InlineQueryResult):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities
parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -124,6 +125,6 @@ class InlineQueryResultAudio(InlineQueryResult):
self.audio_duration: Optional[int] = audio_duration
self.caption: Optional[str] = caption
self.parse_mode: ODVInput[str] = parse_mode
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
self.input_message_content: Optional[InputMessageContent] = input_message_content
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InlineQueryResultCachedAudio."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from telegram._inline.inlinequeryresult import InlineQueryResult
@@ -68,7 +69,7 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities
parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -110,6 +111,6 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
# Optionals
self.caption: Optional[str] = caption
self.parse_mode: ODVInput[str] = parse_mode
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
self.input_message_content: Optional[InputMessageContent] = input_message_content
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InlineQueryResultCachedDocument."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from telegram._inline.inlinequeryresult import InlineQueryResult
@@ -72,7 +73,7 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -120,6 +121,6 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
self.description: Optional[str] = description
self.caption: Optional[str] = caption
self.parse_mode: ODVInput[str] = parse_mode
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
self.input_message_content: Optional[InputMessageContent] = input_message_content
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InlineQueryResultCachedGif."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from telegram._inline.inlinequeryresult import InlineQueryResult
@@ -74,7 +75,7 @@ class InlineQueryResultCachedGif(InlineQueryResult):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -124,7 +125,7 @@ class InlineQueryResultCachedGif(InlineQueryResult):
self.title: Optional[str] = title
self.caption: Optional[str] = caption
self.parse_mode: ODVInput[str] = parse_mode
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
self.input_message_content: Optional[InputMessageContent] = input_message_content
self.show_caption_above_media: Optional[bool] = show_caption_above_media
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InlineQueryResultMpeg4Gif."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from telegram._inline.inlinequeryresult import InlineQueryResult
@@ -74,7 +75,7 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -124,7 +125,7 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
self.title: Optional[str] = title
self.caption: Optional[str] = caption
self.parse_mode: ODVInput[str] = parse_mode
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
self.input_message_content: Optional[InputMessageContent] = input_message_content
self.show_caption_above_media: Optional[bool] = show_caption_above_media
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InlineQueryResultPhoto"""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from telegram._inline.inlinequeryresult import InlineQueryResult
@@ -76,7 +77,7 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -129,7 +130,7 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
self.description: Optional[str] = description
self.caption: Optional[str] = caption
self.parse_mode: ODVInput[str] = parse_mode
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
self.input_message_content: Optional[InputMessageContent] = input_message_content
self.show_caption_above_media: Optional[bool] = show_caption_above_media
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InlineQueryResultCachedVideo."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from telegram._inline.inlinequeryresult import InlineQueryResult
@@ -72,7 +73,7 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -125,7 +126,7 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
self.description: Optional[str] = description
self.caption: Optional[str] = caption
self.parse_mode: ODVInput[str] = parse_mode
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
self.input_message_content: Optional[InputMessageContent] = input_message_content
self.show_caption_above_media: Optional[bool] = show_caption_above_media
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InlineQueryResultCachedVoice."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from telegram._inline.inlinequeryresult import InlineQueryResult
@@ -70,7 +71,7 @@ class InlineQueryResultCachedVoice(InlineQueryResult):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities
parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities|
.. versionchanged:: 20.0
@@ -115,6 +116,6 @@ class InlineQueryResultCachedVoice(InlineQueryResult):
# Optionals
self.caption: Optional[str] = caption
self.parse_mode: ODVInput[str] = parse_mode
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
self.input_message_content: Optional[InputMessageContent] = input_message_content
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InlineQueryResultDocument"""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from telegram._inline.inlinequeryresult import InlineQueryResult
@@ -86,7 +87,7 @@ class InlineQueryResultDocument(InlineQueryResult):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -155,7 +156,7 @@ class InlineQueryResultDocument(InlineQueryResult):
# Optionals
self.caption: Optional[str] = caption
self.parse_mode: ODVInput[str] = parse_mode
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.description: Optional[str] = description
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
self.input_message_content: Optional[InputMessageContent] = input_message_content
+4 -3
View File
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InlineQueryResultGif."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from telegram._inline.inlinequeryresult import InlineQueryResult
@@ -102,7 +103,7 @@ class InlineQueryResultGif(InlineQueryResult):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -166,7 +167,7 @@ class InlineQueryResultGif(InlineQueryResult):
self.title: Optional[str] = title
self.caption: Optional[str] = caption
self.parse_mode: ODVInput[str] = parse_mode
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
self.input_message_content: Optional[InputMessageContent] = input_message_content
self.thumbnail_mime_type: Optional[str] = thumbnail_mime_type
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InlineQueryResultMpeg4Gif."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from telegram._inline.inlinequeryresult import InlineQueryResult
@@ -104,7 +105,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters
after entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities|
.. versionchanged:: 20.0
@@ -168,7 +169,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
self.title: Optional[str] = title
self.caption: Optional[str] = caption
self.parse_mode: ODVInput[str] = parse_mode
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
self.input_message_content: Optional[InputMessageContent] = input_message_content
self.thumbnail_mime_type: Optional[str] = thumbnail_mime_type
+4 -3
View File
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InlineQueryResultPhoto."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from telegram._inline.inlinequeryresult import InlineQueryResult
@@ -92,7 +93,7 @@ class InlineQueryResultPhoto(InlineQueryResult):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
entities parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -154,7 +155,7 @@ class InlineQueryResultPhoto(InlineQueryResult):
self.description: Optional[str] = description
self.caption: Optional[str] = caption
self.parse_mode: ODVInput[str] = parse_mode
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
self.input_message_content: Optional[InputMessageContent] = input_message_content
self.show_caption_above_media: Optional[bool] = show_caption_above_media
+4 -3
View File
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InlineQueryResultVideo."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from telegram._inline.inlinequeryresult import InlineQueryResult
@@ -99,7 +100,7 @@ class InlineQueryResultVideo(InlineQueryResult):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities
parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional.
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional.
|captionentitiesattr|
.. versionchanged:: 20.0
@@ -171,7 +172,7 @@ class InlineQueryResultVideo(InlineQueryResult):
# Optional
self.caption: Optional[str] = caption
self.parse_mode: ODVInput[str] = parse_mode
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.video_width: Optional[int] = video_width
self.video_height: Optional[int] = video_height
self.video_duration: Optional[int] = video_duration
+4 -3
View File
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InlineQueryResultVoice."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from telegram._inline.inlinequeryresult import InlineQueryResult
@@ -72,7 +73,7 @@ class InlineQueryResultVoice(InlineQueryResult):
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities
parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -121,6 +122,6 @@ class InlineQueryResultVoice(InlineQueryResult):
self.voice_duration: Optional[int] = voice_duration
self.caption: Optional[str] = caption
self.parse_mode: ODVInput[str] = parse_mode
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
self.input_message_content: Optional[InputMessageContent] = input_message_content
@@ -17,7 +17,8 @@
# 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 a class that represents a Telegram InputInvoiceMessageContent."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inputmessagecontent import InputMessageContent
from telegram._payment.labeledprice import LabeledPrice
@@ -122,7 +123,7 @@ class InputInvoiceMessageContent(InputMessageContent):
currency (:obj:`str`): Three-letter ISO 4217 currency code, see more on
`currencies <https://core.telegram.org/bots/payments#supported-currencies>`_.
Pass ``XTR`` for payments in |tg_stars|.
prices (Tuple[:class:`telegram.LabeledPrice`]): Price breakdown, a list of
prices (tuple[:class:`telegram.LabeledPrice`]): Price breakdown, a list of
components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus,
etc.). Must contain exactly one item for payments in |tg_stars|.
@@ -135,7 +136,7 @@ class InputInvoiceMessageContent(InputMessageContent):
`currencies.json <https://core.telegram.org/bots/payments/currencies.json>`_, it
shows the number of digits past the decimal point for each currency (2 for the majority
of currencies). Defaults to ``0``. Not supported for payments in |tg_stars|.
suggested_tip_amounts (Tuple[:obj:`int`]): Optional. An array of suggested
suggested_tip_amounts (tuple[:obj:`int`]): Optional. An array of suggested
amounts of tip in the *smallest units* of the currency (integer, **not** float/double).
At most 4 suggested tip amounts can be specified. The suggested tip amounts must be
positive, passed in a strictly increased order and must not exceed
@@ -226,10 +227,10 @@ class InputInvoiceMessageContent(InputMessageContent):
self.payload: str = payload
self.provider_token: Optional[str] = provider_token
self.currency: str = currency
self.prices: Tuple[LabeledPrice, ...] = parse_sequence_arg(prices)
self.prices: tuple[LabeledPrice, ...] = parse_sequence_arg(prices)
# Optionals
self.max_tip_amount: Optional[int] = max_tip_amount
self.suggested_tip_amounts: Tuple[int, ...] = parse_sequence_arg(suggested_tip_amounts)
self.suggested_tip_amounts: tuple[int, ...] = parse_sequence_arg(suggested_tip_amounts)
self.provider_data: Optional[str] = provider_data
self.photo_url: Optional[str] = photo_url
self.photo_size: Optional[int] = photo_size
+4 -3
View File
@@ -17,7 +17,8 @@
# 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 the classes that represent Telegram InputTextMessageContent."""
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional
from telegram._inline.inputmessagecontent import InputMessageContent
from telegram._messageentity import MessageEntity
@@ -75,7 +76,7 @@ class InputTextMessageContent(InputMessageContent):
:tg-const:`telegram.constants.MessageLimit.MAX_TEXT_LENGTH` characters after entities
parsing.
parse_mode (:obj:`str`): Optional. |parse_mode|
entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr|
.. versionchanged:: 20.0
@@ -107,7 +108,7 @@ class InputTextMessageContent(InputMessageContent):
self.message_text: str = message_text
# Optionals
self.parse_mode: ODVInput[str] = parse_mode
self.entities: Tuple[MessageEntity, ...] = parse_sequence_arg(entities)
self.entities: tuple[MessageEntity, ...] = parse_sequence_arg(entities)
self.link_preview_options: ODVInput[LinkPreviewOptions] = parse_lpo_and_dwpp(
disable_web_page_preview, link_preview_options
)
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2024
# 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 an object that represents a Telegram Prepared inline Message."""
import datetime as dtm
from typing import TYPE_CHECKING, Optional
from telegram._telegramobject import TelegramObject
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
from telegram._utils.types import JSONDict
if TYPE_CHECKING:
from telegram import Bot
class PreparedInlineMessage(TelegramObject):
"""Describes an inline message to be sent by a user of a Mini App.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`id` is equal.
.. versionadded:: 21.8
Args:
id (:obj:`str`): Unique identifier of the prepared message
expiration_date (:class:`datetime.datetime`): Expiration date of the prepared message.
Expired prepared messages can no longer be used.
|datetime_localization|
Attributes:
id (:obj:`str`): Unique identifier of the prepared message
expiration_date (:class:`datetime.datetime`): Expiration date of the prepared message.
Expired prepared messages can no longer be used.
|datetime_localization|
"""
__slots__ = ("expiration_date", "id")
def __init__(
self,
id: str, # pylint: disable=redefined-builtin
expiration_date: dtm.datetime,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(api_kwargs=api_kwargs)
self.id: str = id
self.expiration_date: dtm.datetime = expiration_date
self._id_attrs = (self.id,)
self._freeze()
@classmethod
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["PreparedInlineMessage"]:
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
if data is None:
return None
# Get the local timezone from the bot if it has defaults
loc_tzinfo = extract_tzinfo_from_defaults(bot)
data["expiration_date"] = from_timestamp(data.get("expiration_date"), tzinfo=loc_tzinfo)
return super().de_json(data=data, bot=bot)
+3 -3
View File
@@ -17,7 +17,7 @@
# 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 objects related to Telegram menu buttons."""
from typing import TYPE_CHECKING, Dict, Final, Optional, Type
from typing import TYPE_CHECKING, Final, Optional
from telegram import constants
from telegram._telegramobject import TelegramObject
@@ -76,7 +76,7 @@ class MenuButton(TelegramObject):
care of selecting the correct subclass.
Args:
data (Dict[:obj:`str`, ...]): The JSON data.
data (dict[:obj:`str`, ...]): The JSON data.
bot (:class:`telegram.Bot`, optional): The bot associated with this object. Defaults to
:obj:`None`, in which case shortcut methods will not be available.
@@ -95,7 +95,7 @@ class MenuButton(TelegramObject):
if not data and cls is MenuButton:
return None
_class_mapping: Dict[str, Type[MenuButton]] = {
_class_mapping: dict[str, type[MenuButton]] = {
cls.COMMANDS: MenuButtonCommands,
cls.WEB_APP: MenuButtonWebApp,
cls.DEFAULT: MenuButtonDefault,
+153 -30
View File
@@ -21,8 +21,9 @@
import datetime
import re
from collections.abc import Sequence
from html import escape
from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Tuple, TypedDict, Union
from typing import TYPE_CHECKING, Optional, TypedDict, Union
from telegram._chat import Chat
from telegram._chatbackground import ChatBackground
@@ -104,6 +105,7 @@ if TYPE_CHECKING:
InputMediaDocument,
InputMediaPhoto,
InputMediaVideo,
InputPaidMedia,
InputPollOption,
LabeledPrice,
MessageId,
@@ -279,7 +281,10 @@ class Message(MaybeInaccessibleMessage):
and notice that some positional arguments changed position as a result.
Args:
message_id (:obj:`int`): Unique message identifier inside this chat.
message_id (:obj:`int`): Unique message identifier inside this chat. In specific instances
(e.g., message containing a video sent to a big chat), the server might automatically
schedule a message instead of sending it immediately. In such cases, this field will be
``0`` and the relevant message will be unusable until it is actually sent.
from_user (:class:`telegram.User`, optional): Sender of the message; may be empty for
messages sent to channels. For backward compatibility, if the message was sent on
behalf of a chat, the field contains a fake sender user in non-channel chats.
@@ -589,7 +594,10 @@ class Message(MaybeInaccessibleMessage):
.. versionadded:: 21.4
Attributes:
message_id (:obj:`int`): Unique message identifier inside this chat.
message_id (:obj:`int`): Unique message identifier inside this chat. In specific instances
(e.g., message containing a video sent to a big chat), the server might automatically
schedule a message instead of sending it immediately. In such cases, this field will be
``0`` and the relevant message will be unusable until it is actually sent.
from_user (:class:`telegram.User`): Optional. Sender of the message; may be empty for
messages sent to channels. For backward compatibility, if the message was sent on
behalf of a chat, the field contains a fake sender user in non-channel chats.
@@ -629,7 +637,7 @@ class Message(MaybeInaccessibleMessage):
message belongs to.
text (:obj:`str`): Optional. For text messages, the actual UTF-8 text of the message,
0-:tg-const:`telegram.constants.MessageLimit.MAX_TEXT_LENGTH` characters.
entities (Tuple[:class:`telegram.MessageEntity`]): Optional. For text messages, special
entities (tuple[:class:`telegram.MessageEntity`]): Optional. For text messages, special
entities like usernames, URLs, bot commands, etc. that appear in the text. See
:attr:`parse_entity` and :attr:`parse_entities` methods for how to use properly.
This list is empty if the message does not contain entities.
@@ -648,7 +656,7 @@ class Message(MaybeInaccessibleMessage):
..versionadded:: 21.3
caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. For messages with a
caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. For messages with a
Caption. Special entities like usernames, URLs, bot commands, etc. that appear in the
caption. See :attr:`Message.parse_caption_entity` and :attr:`parse_caption_entities`
methods for how to use properly. This list is empty if the message does not contain
@@ -675,7 +683,7 @@ class Message(MaybeInaccessibleMessage):
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
game (:class:`telegram.Game`): Optional. Message is a game, information about the game.
:ref:`More about games >> <games-tree>`.
photo (Tuple[:class:`telegram.PhotoSize`]): Optional. Message is a photo, available
photo (tuple[:class:`telegram.PhotoSize`]): Optional. Message is a photo, available
sizes of the photo. This list is empty if the message does not contain a photo.
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
@@ -703,7 +711,7 @@ class Message(MaybeInaccessibleMessage):
about the video message.
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
new_chat_members (Tuple[:class:`telegram.User`]): Optional. New members that were added
new_chat_members (tuple[:class:`telegram.User`]): Optional. New members that were added
to the group or supergroup and information about them (the bot itself may be one of
these members). This list is empty if the message does not contain new chat members.
@@ -722,7 +730,7 @@ class Message(MaybeInaccessibleMessage):
left_chat_member (:class:`telegram.User`): Optional. A member was removed from the group,
information about them (this member may be the bot itself).
new_chat_title (:obj:`str`): Optional. A chat title was changed to this value.
new_chat_photo (Tuple[:class:`telegram.PhotoSize`]): A chat photo was changed to
new_chat_photo (tuple[:class:`telegram.PhotoSize`]): A chat photo was changed to
this value. This list is empty if the message does not contain a new chat photo.
.. versionchanged:: 20.0
@@ -1118,12 +1126,12 @@ class Message(MaybeInaccessibleMessage):
self.edit_date: Optional[datetime.datetime] = edit_date
self.has_protected_content: Optional[bool] = has_protected_content
self.text: Optional[str] = text
self.entities: Tuple[MessageEntity, ...] = parse_sequence_arg(entities)
self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.entities: tuple[MessageEntity, ...] = parse_sequence_arg(entities)
self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities)
self.audio: Optional[Audio] = audio
self.game: Optional[Game] = game
self.document: Optional[Document] = document
self.photo: Tuple[PhotoSize, ...] = parse_sequence_arg(photo)
self.photo: tuple[PhotoSize, ...] = parse_sequence_arg(photo)
self.sticker: Optional[Sticker] = sticker
self.video: Optional[Video] = video
self.voice: Optional[Voice] = voice
@@ -1132,10 +1140,10 @@ class Message(MaybeInaccessibleMessage):
self.contact: Optional[Contact] = contact
self.location: Optional[Location] = location
self.venue: Optional[Venue] = venue
self.new_chat_members: Tuple[User, ...] = parse_sequence_arg(new_chat_members)
self.new_chat_members: tuple[User, ...] = parse_sequence_arg(new_chat_members)
self.left_chat_member: Optional[User] = left_chat_member
self.new_chat_title: Optional[str] = new_chat_title
self.new_chat_photo: Tuple[PhotoSize, ...] = parse_sequence_arg(new_chat_photo)
self.new_chat_photo: tuple[PhotoSize, ...] = parse_sequence_arg(new_chat_photo)
self.delete_chat_photo: Optional[bool] = bool(delete_chat_photo)
self.group_chat_created: Optional[bool] = bool(group_chat_created)
self.supergroup_chat_created: Optional[bool] = bool(supergroup_chat_created)
@@ -1406,7 +1414,7 @@ class Message(MaybeInaccessibleMessage):
* :class:`telegram.Invoice`
* :class:`telegram.Location`
* :class:`telegram.PassportData`
* List[:class:`telegram.PhotoSize`]
* list[:class:`telegram.PhotoSize`]
* :class:`telegram.PaidMediaInfo`
* :class:`telegram.Poll`
* :class:`telegram.Sticker`
@@ -1478,7 +1486,7 @@ class Message(MaybeInaccessibleMessage):
def compute_quote_position_and_entities(
self, quote: str, index: Optional[int] = None
) -> Tuple[int, Optional[Tuple[MessageEntity, ...]]]:
) -> tuple[int, Optional[tuple[MessageEntity, ...]]]:
"""
Use this function to compute position and entities of a quote in the message text or
caption. Useful for filling the parameters
@@ -1504,7 +1512,7 @@ class Message(MaybeInaccessibleMessage):
message. If not specified, the first occurrence is used.
Returns:
Tuple[:obj:`int`, :obj:`None` | Tuple[:class:`~telegram.MessageEntity`, ...]]: On
tuple[:obj:`int`, :obj:`None` | tuple[:class:`~telegram.MessageEntity`, ...]]: On
success, a tuple containing information about quote position and entities is returned.
Raises:
@@ -1647,7 +1655,7 @@ class Message(MaybeInaccessibleMessage):
quote: Optional[bool],
reply_to_message_id: Optional[int],
reply_parameters: Optional["ReplyParameters"],
) -> Tuple[Union[str, int], ReplyParameters]:
) -> tuple[Union[str, int], ReplyParameters]:
if quote and do_quote:
raise ValueError("The arguments `quote` and `do_quote` are mutually exclusive")
@@ -1714,6 +1722,7 @@ class Message(MaybeInaccessibleMessage):
link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1779,6 +1788,7 @@ class Message(MaybeInaccessibleMessage):
api_kwargs=api_kwargs,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_markdown(
@@ -1792,6 +1802,7 @@ class Message(MaybeInaccessibleMessage):
link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1863,6 +1874,7 @@ class Message(MaybeInaccessibleMessage):
api_kwargs=api_kwargs,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_markdown_v2(
@@ -1876,6 +1888,7 @@ class Message(MaybeInaccessibleMessage):
link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -1943,6 +1956,7 @@ class Message(MaybeInaccessibleMessage):
api_kwargs=api_kwargs,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_html(
@@ -1956,6 +1970,7 @@ class Message(MaybeInaccessibleMessage):
link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2023,6 +2038,7 @@ class Message(MaybeInaccessibleMessage):
api_kwargs=api_kwargs,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_media_group(
@@ -2035,6 +2051,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id: ODVInput[int] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2048,7 +2065,7 @@ class Message(MaybeInaccessibleMessage):
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Optional[Sequence["MessageEntity"]] = None,
) -> Tuple["Message", ...]:
) -> tuple["Message", ...]:
"""Shortcut for::
await bot.send_media_group(
@@ -2075,7 +2092,7 @@ class Message(MaybeInaccessibleMessage):
.. versionadded:: 20.8
Returns:
Tuple[:class:`telegram.Message`]: An array of the sent Messages.
tuple[:class:`telegram.Message`]: An array of the sent Messages.
Raises:
:class:`telegram.error.TelegramError`
@@ -2102,6 +2119,7 @@ class Message(MaybeInaccessibleMessage):
caption_entities=caption_entities,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_photo(
@@ -2117,6 +2135,7 @@ class Message(MaybeInaccessibleMessage):
has_spoiler: Optional[bool] = None,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
show_caption_above_media: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
@@ -2184,6 +2203,7 @@ class Message(MaybeInaccessibleMessage):
has_spoiler=has_spoiler,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
show_caption_above_media=show_caption_above_media,
)
@@ -2203,6 +2223,7 @@ class Message(MaybeInaccessibleMessage):
thumbnail: Optional[FileInput] = None,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2272,6 +2293,7 @@ class Message(MaybeInaccessibleMessage):
thumbnail=thumbnail,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_document(
@@ -2288,6 +2310,7 @@ class Message(MaybeInaccessibleMessage):
thumbnail: Optional[FileInput] = None,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2355,6 +2378,7 @@ class Message(MaybeInaccessibleMessage):
thumbnail=thumbnail,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_animation(
@@ -2374,6 +2398,7 @@ class Message(MaybeInaccessibleMessage):
thumbnail: Optional[FileInput] = None,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
show_caption_above_media: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
@@ -2445,6 +2470,7 @@ class Message(MaybeInaccessibleMessage):
thumbnail=thumbnail,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
show_caption_above_media=show_caption_above_media,
)
@@ -2458,6 +2484,7 @@ class Message(MaybeInaccessibleMessage):
emoji: Optional[str] = None,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2519,6 +2546,7 @@ class Message(MaybeInaccessibleMessage):
emoji=emoji,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_video(
@@ -2539,6 +2567,7 @@ class Message(MaybeInaccessibleMessage):
thumbnail: Optional[FileInput] = None,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
show_caption_above_media: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
@@ -2611,6 +2640,7 @@ class Message(MaybeInaccessibleMessage):
thumbnail=thumbnail,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
show_caption_above_media=show_caption_above_media,
)
@@ -2626,6 +2656,7 @@ class Message(MaybeInaccessibleMessage):
thumbnail: Optional[FileInput] = None,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2691,6 +2722,7 @@ class Message(MaybeInaccessibleMessage):
thumbnail=thumbnail,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_voice(
@@ -2706,6 +2738,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id: ODVInput[int] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2772,6 +2805,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id=message_thread_id,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_location(
@@ -2788,6 +2822,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id: ODVInput[int] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2855,6 +2890,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id=message_thread_id,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_venue(
@@ -2873,6 +2909,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id: ODVInput[int] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -2942,6 +2979,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id=message_thread_id,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_contact(
@@ -2956,6 +2994,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id: ODVInput[int] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -3021,6 +3060,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id=message_thread_id,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_poll(
@@ -3045,6 +3085,7 @@ class Message(MaybeInaccessibleMessage):
question_parse_mode: ODVInput[str] = DEFAULT_NONE,
question_entities: Optional[Sequence["MessageEntity"]] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -3118,6 +3159,7 @@ class Message(MaybeInaccessibleMessage):
question_parse_mode=question_parse_mode,
question_entities=question_entities,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_dice(
@@ -3129,6 +3171,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id: ODVInput[int] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -3189,6 +3232,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id=message_thread_id,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_chat_action(
@@ -3244,6 +3288,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id: ODVInput[int] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -3306,6 +3351,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id=message_thread_id,
business_connection_id=self.business_connection_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_invoice(
@@ -3337,6 +3383,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id: ODVInput[int] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
message_effect_id: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -3427,6 +3474,7 @@ class Message(MaybeInaccessibleMessage):
protect_content=protect_content,
message_thread_id=message_thread_id,
message_effect_id=message_effect_id,
allow_paid_broadcast=allow_paid_broadcast,
)
async def forward(
@@ -3491,6 +3539,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id: Optional[int] = None,
reply_parameters: Optional["ReplyParameters"] = None,
show_caption_above_media: Optional[bool] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -3536,6 +3585,7 @@ class Message(MaybeInaccessibleMessage):
protect_content=protect_content,
message_thread_id=message_thread_id,
show_caption_above_media=show_caption_above_media,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_copy(
@@ -3551,6 +3601,7 @@ class Message(MaybeInaccessibleMessage):
message_thread_id: ODVInput[int] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
show_caption_above_media: Optional[bool] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
@@ -3615,6 +3666,78 @@ class Message(MaybeInaccessibleMessage):
protect_content=protect_content,
message_thread_id=message_thread_id,
show_caption_above_media=show_caption_above_media,
allow_paid_broadcast=allow_paid_broadcast,
)
async def reply_paid_media(
self,
star_count: int,
media: Sequence["InputPaidMedia"],
caption: Optional[str] = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Optional[Sequence["MessageEntity"]] = None,
show_caption_above_media: Optional[bool] = None,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
protect_content: ODVInput[bool] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
reply_markup: Optional[ReplyMarkup] = None,
payload: Optional[str] = None,
allow_paid_broadcast: Optional[bool] = None,
*,
reply_to_message_id: Optional[int] = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
do_quote: Optional[Union[bool, _ReplyKwargs]] = None,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> "Message":
"""Shortcut for::
await bot.send_paid_media(
chat_id=message.chat.id,
business_connection_id=message.business_connection_id,
*args,
**kwargs
)
For the documentation of the arguments, please see :meth:`telegram.Bot.send_paid_media`.
.. versionadded:: 21.7
Keyword Args:
do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote|
Mutually exclusive with :paramref:`quote`.
Returns:
:class:`telegram.Message`: On success, the sent message is returned.
"""
chat_id, effective_reply_parameters = await self._parse_quote_arguments(
do_quote, None, reply_to_message_id, reply_parameters
)
return await self.get_bot().send_paid_media(
chat_id=chat_id,
caption=caption,
star_count=star_count,
media=media,
payload=payload,
business_connection_id=self.business_connection_id,
parse_mode=parse_mode,
caption_entities=caption_entities,
disable_notification=disable_notification,
reply_parameters=effective_reply_parameters,
allow_sending_without_reply=allow_sending_without_reply,
reply_markup=reply_markup,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
protect_content=protect_content,
show_caption_above_media=show_caption_above_media,
allow_paid_broadcast=allow_paid_broadcast,
)
async def edit_text(
@@ -3989,7 +4112,7 @@ class Message(MaybeInaccessibleMessage):
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> Tuple["GameHighScore", ...]:
) -> tuple["GameHighScore", ...]:
"""Shortcut for::
await bot.get_game_high_scores(
@@ -4005,7 +4128,7 @@ class Message(MaybeInaccessibleMessage):
behaviour is undocumented and might be changed by Telegram.
Returns:
Tuple[:class:`telegram.GameHighScore`]
tuple[:class:`telegram.GameHighScore`]
"""
return await self.get_bot().get_game_high_scores(
chat_id=self.chat_id,
@@ -4431,7 +4554,7 @@ class Message(MaybeInaccessibleMessage):
return parse_message_entity(self.caption, entity)
def parse_entities(self, types: Optional[List[str]] = None) -> Dict[MessageEntity, str]:
def parse_entities(self, types: Optional[list[str]] = None) -> dict[MessageEntity, str]:
"""
Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`.
It contains entities from this message filtered by their
@@ -4444,21 +4567,21 @@ class Message(MaybeInaccessibleMessage):
See :attr:`parse_entity` for more info.
Args:
types (List[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as
types (list[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as
strings. If the ``type`` attribute of an entity is contained in this list, it will
be returned. Defaults to a list of all types. All types can be found as constants
in :class:`telegram.MessageEntity`.
Returns:
Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to
dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to
the text that belongs to them, calculated based on UTF-16 codepoints.
"""
return parse_message_entities(self.text, self.entities, types=types)
def parse_caption_entities(
self, types: Optional[List[str]] = None
) -> Dict[MessageEntity, str]:
self, types: Optional[list[str]] = None
) -> dict[MessageEntity, str]:
"""
Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`.
It contains entities from this message's caption filtered by their
@@ -4471,13 +4594,13 @@ class Message(MaybeInaccessibleMessage):
codepoints. See :attr:`parse_entity` for more info.
Args:
types (List[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as
types (list[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as
strings. If the ``type`` attribute of an entity is contained in this list, it will
be returned. Defaults to a list of all types. All types can be found as constants
in :class:`telegram.MessageEntity`.
Returns:
Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to
dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to
the text that belongs to them, calculated based on UTF-16 codepoints.
"""
@@ -4487,7 +4610,7 @@ class Message(MaybeInaccessibleMessage):
def _parse_html(
cls,
message_text: Optional[str],
entities: Dict[MessageEntity, str],
entities: dict[MessageEntity, str],
urled: bool = False,
offset: int = 0,
) -> Optional[str]:
@@ -4676,7 +4799,7 @@ class Message(MaybeInaccessibleMessage):
def _parse_markdown(
cls,
message_text: Optional[str],
entities: Dict[MessageEntity, str],
entities: dict[MessageEntity, str],
urled: bool = False,
version: MarkdownVersion = 1,
offset: int = 0,
+21 -18
View File
@@ -20,7 +20,8 @@
import copy
import itertools
from typing import TYPE_CHECKING, Dict, Final, List, Optional, Sequence, Tuple, Union
from collections.abc import Sequence
from typing import TYPE_CHECKING, Final, Optional, Union
from telegram import constants
from telegram._telegramobject import TelegramObject
@@ -44,10 +45,11 @@ class MessageEntity(TelegramObject):
considered equal, if their :attr:`type`, :attr:`offset` and :attr:`length` are equal.
Args:
type (:obj:`str`): Type of the entity. Can be :attr:`MENTION` (@username),
:attr:`HASHTAG` (#hashtag), :attr:`CASHTAG` ($USD), :attr:`BOT_COMMAND`
(/start@jobs_bot), :attr:`URL` (https://telegram.org),
:attr:`EMAIL` (do-not-reply@telegram.org), :attr:`PHONE_NUMBER` (+1-212-555-0123),
type (:obj:`str`): Type of the entity. Can be :attr:`MENTION` (``@username``),
:attr:`HASHTAG` (``#hashtag`` or ``#hashtag@chatusername``), :attr:`CASHTAG` (``$USD``
or ``USD@chatusername``), :attr:`BOT_COMMAND` (``/start@jobs_bot``), :attr:`URL`
(``https://telegram.org``), :attr:`EMAIL` (``do-not-reply@telegram.org``),
:attr:`PHONE_NUMBER` (``+1-212-555-0123``),
:attr:`BOLD` (**bold text**), :attr:`ITALIC` (*italic text*), :attr:`UNDERLINE`
(underlined text), :attr:`STRIKETHROUGH`, :attr:`SPOILER` (spoiler message),
:attr:`BLOCKQUOTE` (block quotation), :attr:`CODE` (monowidth string), :attr:`PRE`
@@ -73,10 +75,11 @@ class MessageEntity(TelegramObject):
.. versionadded:: 20.0
Attributes:
type (:obj:`str`): Type of the entity. Can be :attr:`MENTION` (@username),
:attr:`HASHTAG` (#hashtag), :attr:`CASHTAG` ($USD), :attr:`BOT_COMMAND`
(/start@jobs_bot), :attr:`URL` (https://telegram.org),
:attr:`EMAIL` (do-not-reply@telegram.org), :attr:`PHONE_NUMBER` (+1-212-555-0123),
type (:obj:`str`): Type of the entity. Can be :attr:`MENTION` (``@username``),
:attr:`HASHTAG` (``#hashtag`` or ``#hashtag@chatusername``), :attr:`CASHTAG` (``$USD``
or ``USD@chatusername``), :attr:`BOT_COMMAND` (``/start@jobs_bot``), :attr:`URL`
(``https://telegram.org``), :attr:`EMAIL` (``do-not-reply@telegram.org``),
:attr:`PHONE_NUMBER` (``+1-212-555-0123``),
:attr:`BOLD` (**bold text**), :attr:`ITALIC` (*italic text*), :attr:`UNDERLINE`
(underlined text), :attr:`STRIKETHROUGH`, :attr:`SPOILER` (spoiler message),
:attr:`BLOCKQUOTE` (block quotation), :attr:`CODE` (monowidth string), :attr:`PRE`
@@ -200,7 +203,7 @@ class MessageEntity(TelegramObject):
accumulated_length = 0
# calculate the length of each slice text[:position] in utf-16 accordingly,
# store the position translations
position_translation: Dict[int, int] = {}
position_translation: dict[int, int] = {}
for i, position in enumerate(positions):
last_position = positions[i - 1] if i > 0 else 0
text_slice = text[last_position:position]
@@ -286,8 +289,8 @@ class MessageEntity(TelegramObject):
@classmethod
def concatenate(
cls,
*args: Union[Tuple[str, _SEM], Tuple[str, _SEM, bool]],
) -> Tuple[str, _SEM]:
*args: Union[tuple[str, _SEM], tuple[str, _SEM, bool]],
) -> tuple[str, _SEM]:
"""Utility functionality for concatenating two text along with their formatting entities.
Tip:
@@ -332,8 +335,8 @@ class MessageEntity(TelegramObject):
.. versionadded:: 21.5
Args:
*args (Tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`]] | \
Tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`], :obj:`bool`]):
*args (tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`]] | \
tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`], :obj:`bool`]):
Arbitrary number of tuples containing the text and its entities to concatenate.
If the last element of the tuple is a :obj:`bool`, it is used to determine whether
to adjust the entities to UTF-16 via
@@ -341,11 +344,11 @@ class MessageEntity(TelegramObject):
default.
Returns:
Tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`]]: The concatenated text
tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`]]: The concatenated text
and its entities
"""
output_text = ""
output_entities: List[MessageEntity] = []
output_entities: list[MessageEntity] = []
for arg in args:
text, entities = arg[0], arg[1]
@@ -357,8 +360,8 @@ class MessageEntity(TelegramObject):
return output_text, output_entities
ALL_TYPES: Final[List[str]] = list(constants.MessageEntityType)
"""List[:obj:`str`]: A list of all available message entity types."""
ALL_TYPES: Final[list[str]] = list(constants.MessageEntityType)
"""list[:obj:`str`]: A list of all available message entity types."""
BLOCKQUOTE: Final[str] = constants.MessageEntityType.BLOCKQUOTE
""":const:`telegram.constants.MessageEntityType.BLOCKQUOTE`
+8 -2
View File
@@ -31,10 +31,16 @@ class MessageId(TelegramObject):
considered equal, if their :attr:`message_id` is equal.
Args:
message_id (:obj:`int`): Unique message identifier.
message_id (:obj:`int`): Unique message identifier. In specific instances
(e.g., message containing a video sent to a big chat), the server might automatically
schedule a message instead of sending it immediately. In such cases, this field will be
``0`` and the relevant message will be unusable until it is actually sent.
Attributes:
message_id (:obj:`int`): Unique message identifier.
message_id (:obj:`int`): Unique message identifier. In specific instances
(e.g., message containing a video sent to a big chat), the server might automatically
schedule a message instead of sending it immediately. In such cases, this field will be
``0`` and the relevant message will be unusable until it is actually sent.
"""
__slots__ = ("message_id",)
+2 -2
View File
@@ -18,7 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram MessageOigin."""
import datetime
from typing import TYPE_CHECKING, Dict, Final, Optional, Type
from typing import TYPE_CHECKING, Final, Optional
from telegram import constants
from telegram._chat import Chat
@@ -105,7 +105,7 @@ class MessageOrigin(TelegramObject):
if not data:
return None
_class_mapping: Dict[str, Type[MessageOrigin]] = {
_class_mapping: dict[str, type[MessageOrigin]] = {
cls.USER: MessageOriginUser,
cls.HIDDEN_USER: MessageOriginHiddenUser,
cls.CHAT: MessageOriginChat,
+8 -7
View File
@@ -17,8 +17,9 @@
# 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 an object that represents a Telegram MessageReaction Update."""
from collections.abc import Sequence
from datetime import datetime
from typing import TYPE_CHECKING, Optional, Sequence, Tuple
from typing import TYPE_CHECKING, Optional
from telegram._chat import Chat
from telegram._reaction import ReactionCount, ReactionType
@@ -54,7 +55,7 @@ class MessageReactionCountUpdated(TelegramObject):
message_id (:obj:`int`): Unique message identifier inside the chat.
date (:class:`datetime.datetime`): Date of the change in Unix time
|datetime_localization|
reactions (Tuple[:class:`telegram.ReactionCount`]): List of reactions that are present on
reactions (tuple[:class:`telegram.ReactionCount`]): List of reactions that are present on
the message
"""
@@ -79,7 +80,7 @@ class MessageReactionCountUpdated(TelegramObject):
self.chat: Chat = chat
self.message_id: int = message_id
self.date: datetime = date
self.reactions: Tuple[ReactionCount, ...] = parse_sequence_arg(reactions)
self.reactions: tuple[ReactionCount, ...] = parse_sequence_arg(reactions)
self._id_attrs = (self.chat, self.message_id, self.date, self.reactions)
self._freeze()
@@ -132,9 +133,9 @@ class MessageReactionUpdated(TelegramObject):
message_id (:obj:`int`): Unique message identifier inside the chat.
date (:class:`datetime.datetime`): Date of the change in Unix time.
|datetime_localization|
old_reaction (Tuple[:class:`telegram.ReactionType`]): Previous list of reaction types
old_reaction (tuple[:class:`telegram.ReactionType`]): Previous list of reaction types
that were set by the user.
new_reaction (Tuple[:class:`telegram.ReactionType`]): New list of reaction types that
new_reaction (tuple[:class:`telegram.ReactionType`]): New list of reaction types that
were set by the user.
user (:class:`telegram.User`): Optional. The user that changed the reaction, if the user
isn't anonymous.
@@ -169,8 +170,8 @@ class MessageReactionUpdated(TelegramObject):
self.chat: Chat = chat
self.message_id: int = message_id
self.date: datetime = date
self.old_reaction: Tuple[ReactionType, ...] = parse_sequence_arg(old_reaction)
self.new_reaction: Tuple[ReactionType, ...] = parse_sequence_arg(new_reaction)
self.old_reaction: tuple[ReactionType, ...] = parse_sequence_arg(old_reaction)
self.new_reaction: tuple[ReactionType, ...] = parse_sequence_arg(new_reaction)
# Optional
self.user: Optional[User] = user
+58 -7
View File
@@ -18,12 +18,14 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains objects that represent paid media in Telegram."""
from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type
from collections.abc import Sequence
from typing import TYPE_CHECKING, Final, Optional
from telegram import constants
from telegram._files.photosize import PhotoSize
from telegram._files.video import Video
from telegram._telegramobject import TelegramObject
from telegram._user import User
from telegram._utils import enum
from telegram._utils.argumentparsing import parse_sequence_arg
from telegram._utils.types import JSONDict
@@ -80,7 +82,7 @@ class PaidMedia(TelegramObject):
care of selecting the correct subclass.
Args:
data (Dict[:obj:`str`, ...]): The JSON data.
data (dict[:obj:`str`, ...]): The JSON data.
bot (:class:`telegram.Bot`, optional): The bot associated with this object.
Returns:
@@ -95,7 +97,7 @@ class PaidMedia(TelegramObject):
if not data and cls is PaidMedia:
return None
_class_mapping: Dict[str, Type[PaidMedia]] = {
_class_mapping: dict[str, type[PaidMedia]] = {
cls.PREVIEW: PaidMediaPreview,
cls.PHOTO: PaidMediaPhoto,
cls.VIDEO: PaidMediaVideo,
@@ -164,7 +166,7 @@ class PaidMediaPhoto(PaidMedia):
Attributes:
type (:obj:`str`): Type of the paid media, always :tg-const:`telegram.PaidMedia.PHOTO`.
photo (Tuple[:class:`telegram.PhotoSize`]): The photo.
photo (tuple[:class:`telegram.PhotoSize`]): The photo.
"""
__slots__ = ("photo",)
@@ -178,7 +180,7 @@ class PaidMediaPhoto(PaidMedia):
super().__init__(type=PaidMedia.PHOTO, api_kwargs=api_kwargs)
with self._unfrozen():
self.photo: Tuple[PhotoSize, ...] = parse_sequence_arg(photo)
self.photo: tuple[PhotoSize, ...] = parse_sequence_arg(photo)
self._id_attrs = (self.type, self.photo)
@@ -258,7 +260,7 @@ class PaidMediaInfo(TelegramObject):
Attributes:
star_count (:obj:`int`): The number of Telegram Stars that must be paid to buy access to
the media.
paid_media (Tuple[:class:`telegram.PaidMedia`]): Information about the paid media.
paid_media (tuple[:class:`telegram.PaidMedia`]): Information about the paid media.
"""
__slots__ = ("paid_media", "star_count")
@@ -272,7 +274,7 @@ class PaidMediaInfo(TelegramObject):
) -> None:
super().__init__(api_kwargs=api_kwargs)
self.star_count: int = star_count
self.paid_media: Tuple[PaidMedia, ...] = parse_sequence_arg(paid_media)
self.paid_media: tuple[PaidMedia, ...] = parse_sequence_arg(paid_media)
self._id_attrs = (self.star_count, self.paid_media)
self._freeze()
@@ -288,3 +290,52 @@ class PaidMediaInfo(TelegramObject):
data["paid_media"] = PaidMedia.de_list(data.get("paid_media"), bot=bot)
return super().de_json(data=data, bot=bot)
class PaidMediaPurchased(TelegramObject):
"""This object contains information about a paid media purchase.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`from_user` and :attr:`paid_media_payload` are equal.
Note:
In Python :keyword:`from` is a reserved word. Use :paramref:`from_user` instead.
.. versionadded:: 21.6
Args:
from_user (:class:`telegram.User`): User who purchased the media.
paid_media_payload (:obj:`str`): Bot-specified paid media payload.
Attributes:
from_user (:class:`telegram.User`): User who purchased the media.
paid_media_payload (:obj:`str`): Bot-specified paid media payload.
"""
__slots__ = ("from_user", "paid_media_payload")
def __init__(
self,
from_user: "User",
paid_media_payload: str,
*,
api_kwargs: Optional[JSONDict] = None,
) -> None:
super().__init__(api_kwargs=api_kwargs)
self.from_user: User = from_user
self.paid_media_payload: str = paid_media_payload
self._id_attrs = (self.from_user, self.paid_media_payload)
self._freeze()
@classmethod
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["PaidMediaPurchased"]:
data = cls._parse_data(data)
if not data:
return None
data["from_user"] = User.de_json(data=data.pop("from"), bot=bot)
return super().de_json(data=data, bot=bot)
+8 -7
View File
@@ -19,7 +19,8 @@
# pylint: disable=missing-module-docstring, redefined-builtin
import json
from base64 import b64decode
from typing import TYPE_CHECKING, Optional, Sequence, Tuple, no_type_check
from collections.abc import Sequence
from typing import TYPE_CHECKING, Optional, no_type_check
try:
from cryptography.hazmat.backends import default_backend
@@ -390,11 +391,11 @@ class SecureValue(TelegramObject):
selfie (:class:`telegram.FileCredentials`, optional): Credentials for encrypted selfie
of the user with a document. Can be available for "passport", "driver_license",
"identity_card" and "internal_passport".
translation (List[:class:`telegram.FileCredentials`], optional): Credentials for an
translation (list[:class:`telegram.FileCredentials`], optional): Credentials for an
encrypted translation of the document. Available for "passport", "driver_license",
"identity_card", "internal_passport", "utility_bill", "bank_statement",
"rental_agreement", "passport_registration" and "temporary_registration".
files (List[:class:`telegram.FileCredentials`], optional): Credentials for encrypted
files (list[:class:`telegram.FileCredentials`], optional): Credentials for encrypted
files. Available for "utility_bill", "bank_statement", "rental_agreement",
"passport_registration" and "temporary_registration" types.
@@ -410,7 +411,7 @@ class SecureValue(TelegramObject):
selfie (:class:`telegram.FileCredentials`): Optional. Credentials for encrypted selfie
of the user with a document. Can be available for "passport", "driver_license",
"identity_card" and "internal_passport".
translation (Tuple[:class:`telegram.FileCredentials`]): Optional. Credentials for an
translation (tuple[:class:`telegram.FileCredentials`]): Optional. Credentials for an
encrypted translation of the document. Available for "passport", "driver_license",
"identity_card", "internal_passport", "utility_bill", "bank_statement",
"rental_agreement", "passport_registration" and "temporary_registration".
@@ -418,7 +419,7 @@ class SecureValue(TelegramObject):
.. versionchanged:: 20.0
|tupleclassattrs|
files (Tuple[:class:`telegram.FileCredentials`]): Optional. Credentials for encrypted
files (tuple[:class:`telegram.FileCredentials`]): Optional. Credentials for encrypted
files. Available for "utility_bill", "bank_statement", "rental_agreement",
"passport_registration" and "temporary_registration" types.
@@ -447,8 +448,8 @@ class SecureValue(TelegramObject):
self.front_side: Optional[FileCredentials] = front_side
self.reverse_side: Optional[FileCredentials] = reverse_side
self.selfie: Optional[FileCredentials] = selfie
self.files: Tuple[FileCredentials, ...] = parse_sequence_arg(files)
self.translation: Tuple[FileCredentials, ...] = parse_sequence_arg(translation)
self.files: tuple[FileCredentials, ...] = parse_sequence_arg(files)
self.translation: tuple[FileCredentials, ...] = parse_sequence_arg(translation)
self._freeze()

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