Compare commits

...

35 Commits

Author SHA1 Message Date
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
Bibo-Joshi 8c692d1008 Bump Version to v21.5 (#4449) 2024-09-01 15:25:34 +02:00
Bibo-Joshi 970d2ab085 Documentation Improvements (#4400, #4448)
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
Co-authored-by: Palaptin <100526200+Palaptin@users.noreply.github.com>
2024-09-01 14:12:41 +02:00
dependabot[bot] 60b439ff42 Update cachetools requirement from <5.5.0,>=5.3.3 to >=5.3.3,<5.6.0 (#4437)
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-09-01 10:35:11 +02:00
Bibo-Joshi b17b0d248d Improve PyPI Automation (#4375) 2024-09-01 09:34:20 +02:00
Bibo-Joshi e0f36867cc Add MessageEntity.shift_entities and MessageEntity.concatenate (#4376) 2024-09-01 09:33:12 +02:00
Poolitzer 01f689373c Bot API 7.9 (#4429)
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
2024-09-01 09:32:42 +02:00
Bibo-Joshi 1e05381133 Update Test Suite to New Test Channel Setup (#4435) 2024-08-26 20:16:35 +02:00
Martin Hjelmare a05362c79a Remove Surplus Logging from Updater Network Loop (#4432) 2024-08-19 16:40:28 +02:00
Palaptin fbf07bf126 Improve Fixture Usage in test_message.py (#4431) 2024-08-19 16:14:01 +02:00
Harshil 3017bf00a4 Update Python 3.13 Test Suite to RC1 (#4415) 2024-08-13 17:58:08 +02:00
dependabot[bot] 374875c786 Bump sphinx from 7.4.7 to 8.0.2 and furo from 2024.7.18 to 2024.8.6 (#4412)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
2024-08-13 17:57:26 +02:00
Harshil 8f9db63f4f Bump ruff and Add New Rules (#4416)
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
2024-08-07 21:56:46 +02:00
Harshil 1787586902 Bugfix for "Available In" Admonitions (#4413) 2024-08-03 22:47:38 +02:00
dependabot[bot] 9c50a38512 Bump test-summary/action from 2.3 to 2.4 (#4410)
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: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
2024-08-03 17:27:39 +02:00
Bibo-Joshi 3a49372591 Add Parameter read_file_handle to InputFile (#4388) 2024-08-02 22:28:38 +02:00
dependabot[bot] e637d1733c Bump pytest from 8.2.2 to 8.3.2 (#4403)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-02 14:01:31 +02:00
dependabot[bot] b89f5d6126 Bump dependabot/fetch-metadata from 2.1.0 to 2.2.0 (#4411)
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: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
2024-08-02 14:00:26 +02:00
Bibo-Joshi 6578c76068 API 7.8 (#4408) 2024-08-02 13:43:27 +02:00
Bibo-Joshi a967dbe37a Document Return Types of RequestData Members (#4396) 2024-08-02 13:41:39 +02:00
dependabot[bot] af76a8485f Update cachetools requirement from ~=5.3.3 to >=5.3.3,<5.5.0 (#4390)
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-07-24 21:03:54 +02:00
MOHD YUSUF 8a205b10c0 Add Introductory Paragraphs to Telegram Types Subsections (#4389)
Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
2024-07-24 21:02:53 +02:00
dependabot[bot] 6d70c56159 Bump sphinx from 7.3.7 to 7.4.7 (#4395)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-21 21:23:21 +02:00
Pablo Martínez 0913b859d7 Add Internal Constants for Encodings (#4378) 2024-07-21 21:13:30 +02:00
Bibo-Joshi c3f17bb18e Start Adapting to RTD Addons (#4386) 2024-07-21 21:12:30 +02:00
dependabot[bot] 006a290b7b Bump furo from 2024.5.6 to 2024.7.18 (#4392)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-21 21:05:01 +02:00
Jãїиãм 422993b8ab Add Parameter game_pattern to CallbackQueryHandler (#4353) 2024-07-13 21:26:44 +02:00
265 changed files with 4746 additions and 2732 deletions
+1 -1
View File
@@ -210,7 +210,7 @@ doc strings don't have a separate documentation site they generate, instead, the
User facing documentation
-------------------------
We use `sphinx`_ to generate static HTML docs. To build them, first make sure you're running Python 3.9 or above and have the required dependencies installed as explained above.
We use `sphinx`_ to generate static HTML docs. To build them, first make sure you're running Python 3.10 or above and have the required dependencies installed as explained above.
Then, run the following from the PTB root directory:
.. code-block:: bash
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
- name: Fetch Dependabot metadata
id: dependabot-metadata
uses: dependabot/fetch-metadata@v2.1.0
uses: dependabot/fetch-metadata@v2.2.0
- uses: actions/checkout@v4
with:
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ${{matrix.os}}
strategy:
matrix:
python-version: [3.9]
python-version: [3.10]
os: [ubuntu-latest]
fail-fast: False
steps:
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
runs-on: ${{matrix.os}}
strategy:
matrix:
python-version: [3.9]
python-version: ['3.10']
os: [ubuntu-latest]
fail-fast: False
steps:
+16 -91
View File
@@ -1,17 +1,15 @@
name: Publish to PyPI
on:
# Run on any tag
push:
tags:
- '**'
# manually trigger the workflow - for testing only
# manually trigger the workflow
workflow_dispatch:
jobs:
build:
name: Build Distribution
runs-on: ubuntu-latest
outputs:
TAG: ${{ steps.get_tag.outputs.TAG }}
steps:
- uses: actions/checkout@v4
@@ -29,11 +27,15 @@ jobs:
with:
name: python-package-distributions
path: dist/
- name: Get Tag Name
id: get_tag
run: |
pip install .
TAG=$(python -c "from telegram import __version__; print(f'v{__version__}')")
echo "TAG=$TAG" >> $GITHUB_OUTPUT
publish-to-pypi:
name: Publish to PyPI
# only publish to PyPI on tag pushes
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
needs:
- build
runs-on: ubuntu-latest
@@ -52,42 +54,11 @@ jobs:
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
publish-to-test-pypi:
name: Publish to Test PyPI
needs:
- build
runs-on: ubuntu-latest
environment:
name: release_test_pypi
url: https://test.pypi.org/p/python-telegram-bot
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Publish to Test PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
compute-signatures:
name: Compute SHA1 Sums and Sign with Sigstore
runs-on: ubuntu-latest
needs:
- publish-to-pypi
- publish-to-test-pypi
# run if either of the publishing jobs ran successfully
# see also:
# https://github.com/actions/runner/issues/491#issuecomment-850884422
if: |
always() && (
(needs.publish-to-pypi.result == 'success') ||
(needs.publish-to-test-pypi.result == 'success')
)
permissions:
id-token: write # IMPORTANT: mandatory for sigstore
@@ -106,7 +77,7 @@ jobs:
sha1sum $file > $file.sha1
done
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@v2.1.1
uses: sigstore/gh-action-sigstore-python@v3.0.0
with:
inputs: >-
./dist/*.tar.gz
@@ -120,13 +91,8 @@ jobs:
github-release:
name: Upload to GitHub Release
needs:
- publish-to-pypi
- build
- compute-signatures
if: |
always() && (
(needs.publish-to-pypi.result == 'success') &&
(needs.compute-signatures.result == 'success')
)
runs-on: ubuntu-latest
@@ -142,63 +108,22 @@ jobs:
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
# Create a GitHub Release for this tag. The description can be changed later, as for now
TAG: ${{ needs.build.outputs.TAG }}
# Create a tag and a GitHub Release. The description can be changed later, as for now
# we don't define it through this workflow.
run: >-
gh release create
'${{ github.ref_name }}'
'${{ env.TAG }}'
--repo '${{ github.repository }}'
--generate-notes
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
TAG: ${{ needs.build.outputs.TAG }}
# Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates.
run: >-
gh release upload
'${{ github.ref_name }}' dist/**
--repo '${{ github.repository }}'
github-test-release:
name: Upload to GitHub Release Draft
needs:
- publish-to-test-pypi
- compute-signatures
if: |
always() && (
(needs.publish-to-test-pypi.result == 'success') &&
(needs.compute-signatures.result == 'success')
)
runs-on: ubuntu-latest
permissions:
contents: write # IMPORTANT: mandatory for making GitHub Releases
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions-and-signatures
path: dist/
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
# Create a GitHub Release *draft*. The description can be changed later, as for now
# we don't define it through this workflow.
run: >-
gh release create
'${{ github.ref_name }}'
--repo '${{ github.repository }}'
--generate-notes
--draft
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
# Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates.
run: >-
gh release upload
'${{ github.ref_name }}' dist/**
'${{ env.TAG }}' dist/**
--repo '${{ github.repository }}'
+132
View File
@@ -0,0 +1,132 @@
name: Publish to Test PyPI
on:
# manually trigger the workflow
workflow_dispatch:
jobs:
build:
name: Build Distribution
runs-on: ubuntu-latest
outputs:
TAG: ${{ steps.get_tag.outputs.TAG }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install pypa/build
run: >-
python3 -m pip install build --user
- name: Build a binary wheel and a source tarball
run: python3 -m build
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Get Tag Name
id: get_tag
run: |
pip install .
TAG=$(python -c "from telegram import __version__; print(f'v{__version__}')")
echo "TAG=$TAG" >> $GITHUB_OUTPUT
publish-to-test-pypi:
name: Publish to Test PyPI
needs:
- build
runs-on: ubuntu-latest
environment:
name: release_test_pypi
url: https://test.pypi.org/p/python-telegram-bot
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Publish to Test PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
compute-signatures:
name: Compute SHA1 Sums and Sign with Sigstore
runs-on: ubuntu-latest
needs:
- publish-to-test-pypi
permissions:
id-token: write # IMPORTANT: mandatory for sigstore
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Compute SHA1 Sums
run: |
# Compute SHA1 sum of the distribution packages and save it to a file with the same name,
# but with .sha1 extension
for file in dist/*; do
sha1sum $file > $file.sha1
done
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@v3.0.0
with:
inputs: >-
./dist/*.tar.gz
./dist/*.whl
- name: Store the distribution packages and signatures
uses: actions/upload-artifact@v4
with:
name: python-package-distributions-and-signatures
path: dist/
github-test-release:
name: Upload to GitHub Release Draft
needs:
- build
- compute-signatures
runs-on: ubuntu-latest
permissions:
contents: write # IMPORTANT: mandatory for making GitHub Releases
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions-and-signatures
path: dist/
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
TAG: ${{ needs.build.outputs.TAG }}
# Create a GitHub Release *draft*. The description can be changed later, as for now
# we don't define it through this workflow.
run: >-
gh release create
'${{ env.TAG }}'
--repo '${{ github.repository }}'
--generate-notes
--draft
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
TAG: ${{ needs.build.outputs.TAG }}
# Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates.
run: >-
gh release upload
'${{ env.TAG }}' dist/**
--repo '${{ github.repository }}'
+1 -1
View File
@@ -41,7 +41,7 @@ jobs:
- name: Test Summary
id: test_summary
uses: test-summary/action@v2.3
uses: test-summary/action@v2.4
if: always() # always run, even if tests fail
with:
paths: .test_report_official.xml
@@ -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.0
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)
+2 -2
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-beta.3']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13.0-rc.2']
os: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: False
steps:
@@ -83,7 +83,7 @@ jobs:
- name: Test Summary
id: test_summary
uses: test-summary/action@v2.3
uses: test-summary/action@v2.4
if: always() # always run, even if tests fail
with:
paths: |
+5 -5
View File
@@ -7,7 +7,7 @@ ci:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.5.0'
rev: 'v0.5.6'
hooks:
- id: ruff
name: ruff
@@ -15,7 +15,7 @@ repos:
- httpx~=0.27
- tornado~=6.4
- APScheduler~=3.10.4
- cachetools~=5.3.3
- cachetools>=5.3.3,<5.5.0
- aiolimiter~=1.1.0
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.4.2
@@ -37,7 +37,7 @@ repos:
- httpx~=0.27
- tornado~=6.4
- APScheduler~=3.10.4
- cachetools~=5.3.3
- cachetools>=5.3.3,<5.5.0
- aiolimiter~=1.1.0
- . # this basically does `pip install -e .`
- repo: https://github.com/pre-commit/mirrors-mypy
@@ -53,7 +53,7 @@ repos:
- httpx~=0.27
- tornado~=6.4
- APScheduler~=3.10.4
- cachetools~=5.3.3
- cachetools>=5.3.3,<5.5.0
- aiolimiter~=1.1.0
- . # this basically does `pip install -e .`
- id: mypy
@@ -65,7 +65,7 @@ repos:
additional_dependencies:
- tornado~=6.4
- APScheduler~=3.10.4
- cachetools~=5.3.3
- cachetools>=5.3.3,<5.5.0
- . # this basically does `pip install -e .`
- repo: https://github.com/asottile/pyupgrade
rev: v3.16.0
+3
View File
@@ -60,6 +60,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `Hugo Damer <https://github.com/HakimusGIT>`_
- `ihoru <https://github.com/ihoru>`_
- `Iulian Onofrei <https://github.com/revolter>`_
- `Jainam Oswal <https://github.com/jainamoswal>`_
- `Jasmin Bom <https://github.com/jsmnbom>`_
- `JASON0916 <https://github.com/JASON0916>`_
- `jeffffc <https://github.com/jeffffc>`_
@@ -86,6 +87,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `Miguel C. R. <https://github.com/MiguelX413>`_
- `miles <https://github.com/miles170>`_
- `Mischa Krüger <https://github.com/Makman2>`_
- `Mohd Yusuf <https://github.com/mohdyusuf2312>`_
- `naveenvhegde <https://github.com/naveenvhegde>`_
- `neurrone <https://github.com/neurrone>`_
- `NikitaPirate <https://github.com/NikitaPirate>`_
@@ -96,6 +98,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `Oleg Sushchenko <https://github.com/feuillemorte>`_
- `Or Bin <https://github.com/OrBin>`_
- `overquota <https://github.com/overquota>`_
- `Pablo Martinez <https://github.com/elpekenin>`_
- `Paradox <https://github.com/paradox70>`_
- `Patrick Hofmann <https://github.com/PH89>`_
- `Paul Larsen <https://github.com/PaulSonOfLars>`_
+83
View File
@@ -4,6 +4,89 @@
Changelog
=========
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
============
*Released 2024-09-01*
This is the technical changelog for version 21.5. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
Major Changes
-------------
- Full Support for Bot API 7.9 (:pr:`4429`)
- Full Support for Bot API 7.8 (:pr:`4408`)
New Features
------------
- Add ``MessageEntity.shift_entities`` and ``MessageEntity.concatenate`` (:pr:`4376` closes :issue:`4372`)
- Add Parameter ``game_pattern`` to ``CallbackQueryHandler`` (:pr:`4353` by `jainamoswal <https://github.com/jainamoswal>`_ closes :issue:`4269`)
- Add Parameter ``read_file_handle`` to ``InputFile`` (:pr:`4388` closes :issue:`4339`)
Documentation Improvements
--------------------------
- Bugfix for "Available In" Admonitions (:pr:`4413`)
- Documentation Improvements (:pr:`4400` closes :issue:`4446`, :pr:`4448` by `Palaptin <https://github.com/Palaptin>`_)
- Document Return Types of ``RequestData`` Members (:pr:`4396`)
- Add Introductory Paragraphs to Telegram Types Subsections (:pr:`4389` by `mohdyusuf2312 <https://github.com/mohdyusuf2312>`_ closes :issue:`4380`)
- Start Adapting to RTD Addons (:pr:`4386`)
Minor and Internal Changes
---------------------------
- Remove Surplus Logging from ``Updater`` Network Loop (:pr:`4432` by `MartinHjelmare <https://github.com/MartinHjelmare>`_)
- Add Internal Constants for Encodings (:pr:`4378` by `elpekenin <https://github.com/elpekenin>`_)
- Improve PyPI Automation (:pr:`4375` closes :issue:`4373`)
- Update Test Suite to New Test Channel Setup (:pr:`4435`)
- Improve Fixture Usage in ``test_message.py`` (:pr:`4431` by `Palaptin <https://github.com/Palaptin>`_)
- Update Python 3.13 Test Suite to RC1 (:pr:`4415`)
- Bump ``ruff`` and Add New Rules (:pr:`4416`)
Dependency Updates
------------------
- Update ``cachetools`` requirement from <5.5.0,>=5.3.3 to >=5.3.3,<5.6.0 (:pr:`4437`)
- Bump ``sphinx`` from 7.4.7 to 8.0.2 and ``furo`` from 2024.7.18 to 2024.8.6 (:pr:`4412`)
- Bump ``test-summary/action`` from 2.3 to 2.4 (:pr:`4410`)
- Bump ``pytest`` from 8.2.2 to 8.3.2 (:pr:`4403`)
- Bump ``dependabot/fetch-metadata`` from 2.1.0 to 2.2.0 (:pr:`4411`)
- Update ``cachetools`` requirement from ~=5.3.3 to >=5.3.3,<5.5.0 (:pr:`4390`)
- Bump ``sphinx`` from 7.3.7 to 7.4.7 (:pr:`4395`)
- Bump ``furo`` from 2024.5.6 to 2024.7.18 (:pr:`4392`)
Version 21.4
============
+5 -5
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.7-blue?logo=telegram
.. image:: https://img.shields.io/badge/Bot%20API-7.10-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API version
@@ -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.7** are natively supported by this library.
All types and methods of the Telegram Bot API **7.10** 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
@@ -119,7 +119,7 @@ Verifying Releases
To enable you to verify that a release file that you downloaded was indeed provided by the ``python-telegram-bot`` team, we have taken the following measures.
Starting with v21.4, all releases are signed via `sigstore <https://sigstore.dev>`_.
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``.
@@ -157,7 +157,7 @@ PTB can be installed with optional dependencies:
* ``pip install "python-telegram-bot[http2]"`` installs `httpx[http2] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to use HTTP/2.
* ``pip install "python-telegram-bot[rate-limiter]"`` installs `aiolimiter~=1.1.0 <https://aiolimiter.readthedocs.io/en/stable/>`_. Use this, if you want to use ``telegram.ext.AIORateLimiter``.
* ``pip install "python-telegram-bot[webhooks]"`` installs the `tornado~=6.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 <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[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``.
To install multiple optional dependencies, separate them by commas, e.g. ``pip install "python-telegram-bot[socks,webhooks]"``.
@@ -232,4 +232,4 @@ 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.
.. _`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>
+4 -3
View File
@@ -140,7 +140,7 @@ class AdmonitionInserter:
r"^\s*(?P<attr_name>[a-z_]+)" # Any number of spaces, named group for attribute
r"\s?\(" # Optional whitespace, opening parenthesis
r".*" # Any number of characters (that could denote a built-in type)
r":class:`.+`" # Marker of a classref, class name in backticks
r":(class|obj):`.+`" # Marker of a classref, class name in backticks
r".*\):" # Any number of characters, closing parenthesis, colon.
# The ^ colon above along with parenthesis is important because it makes sure that
# the class is mentioned in the attribute description, not in free text.
@@ -149,11 +149,11 @@ class AdmonitionInserter:
)
# for properties: there is no attr name in docstring. Just check if there's a class name.
prop_docstring_pattern = re.compile(r":class:`.+`.*:")
prop_docstring_pattern = re.compile(r":(class|obj):`.+`.*:")
# pattern for iterating over potentially many class names in docstring for one attribute.
# Tilde is optional (sometimes it is in the docstring, sometimes not).
single_class_name_pattern = re.compile(r":class:`~?(?P<class_name>[\w.]*)`")
single_class_name_pattern = re.compile(r":(class|obj):`~?(?P<class_name>[\w.]*)`")
classes_to_inspect = inspect.getmembers(telegram, inspect.isclass) + inspect.getmembers(
telegram.ext, inspect.isclass
@@ -366,6 +366,7 @@ class AdmonitionInserter:
# to ".. admonition: Examples":
".. admonition:: Examples",
".. version",
"Args:",
# The space after ":param" is important because docstring can contain
# ":paramref:" in its plain text in the beginning of a line (e.g. ExtBot):
":param ",
+2 -3
View File
@@ -16,6 +16,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/].
import collections.abc
import contextlib
import inspect
import re
import typing
@@ -153,13 +154,11 @@ def autodoc_process_docstring(
if isinstance(obj, telegram.ext.filters.BaseFilter):
obj = obj.__class__
try:
with contextlib.suppress(Exception):
source_lines, start_line = inspect.getsourcelines(obj)
end_line = start_line + len(source_lines)
file = Path(inspect.getsourcefile(obj)).relative_to(FILE_ROOT)
LINE_NUMBERS[name] = (file, start_line, end_line)
except Exception:
pass
# Since we don't document the `__init__`, we call this manually to have it available for
# attributes -- see the note above
+2 -1
View File
@@ -88,7 +88,6 @@ class TGConstXRefRole(PyXRefRole):
refnode.rawsource,
CONSTANTS_ROLE,
)
return title, target
except Exception as exc:
sphinx_logger.exception(
"%s:%d: WARNING: Did not convert reference %s due to an exception.",
@@ -98,3 +97,5 @@ class TGConstXRefRole(PyXRefRole):
exc_info=exc,
)
return title, target
else:
return title, target
+2 -2
View File
@@ -1,5 +1,5 @@
sphinx==7.3.7
furo==2024.5.6
sphinx==8.0.2
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
+10 -2
View File
@@ -1,3 +1,4 @@
import os
import re
import sys
from pathlib import Path
@@ -29,7 +30,7 @@ version = telegram.__version__
release = telegram.__version__
# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = "6.1.3"
needs_sphinx = "8.0.2"
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@@ -251,7 +252,14 @@ htmlhelp_basename = "python-telegram-bot-doc"
# The base URL which points to the root of the HTML documentation. It is used to indicate the
# location of document using The Canonical Link Relation. Default: ''.
html_baseurl = "https://docs.python-telegram-bot.org"
# Set canonical URL from the Read the Docs Domain
html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "")
# Tell Jinja2 templates the build is running on Read the Docs
html_context = {}
if os.environ.get("READTHEDOCS", "") == "True":
html_context["READTHEDOCS"] = True
# -- Options for LaTeX output ---------------------------------------------
+2
View File
@@ -120,6 +120,7 @@ Available Types
telegram.paidmediainfo
telegram.paidmediaphoto
telegram.paidmediapreview
telegram.paidmediapurchased
telegram.paidmediavideo
telegram.photosize
telegram.poll
@@ -130,6 +131,7 @@ Available Types
telegram.reactiontype
telegram.reactiontypecustomemoji
telegram.reactiontypeemoji
telegram.reactiontypepaid
telegram.replykeyboardmarkup
telegram.replykeyboardremove
telegram.replyparameters
@@ -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:
+15
View File
@@ -1,6 +1,21 @@
.. _games-tree:
Games
-----
Your bot can offer users **HTML5 games** to play solo or to compete against each other in groups and one-on-one chats. Create games via `@BotFather <https://telegram.me/BotFather>`_ using the ``/newgame`` command. Please note that this kind of power requires responsibility: you will need to accept the terms for each game that your bots will be offering.
* Games are a new type of content on Telegram, represented by the :class:`telegram.Game` and :class:`telegram.InlineQueryResultGame` objects.
* Once you've created a game via `BotFather <https://t.me/botfather>`_, you can send games to chats as regular messages using the :meth:`~telegram.Bot.sendGame` method, or use :ref:`inline mode <inline-tree>` with :class:`telegram.InlineQueryResultGame`.
* If you send the game message without any buttons, it will automatically have a 'Play ``GameName``' button. When this button is pressed, your bot gets a :class:`telegram.CallbackQuery` with the ``game_short_name`` of the requested game. You provide the correct URL for this particular user and the app opens the game in the in-app browser.
* You can manually add multiple buttons to your game message. Please note that the first button in the first row **must always** launch the game, using the field ``callback_game`` in :class:`telegram.InlineKeyboardButton`. You can add extra buttons according to taste: e.g., for a description of the rules, or to open the game's official community.
* To make your game more attractive, you can upload a GIF animation that demonstrates the game to the users via `BotFather <https://t.me/botfather>`_ (see `Lumberjack <https://t.me/gamebot?game=lumberjack>`_ for example).
* A game message will also display high scores for the current chat. Use :meth:`~telegram.Bot.setGameScore` to post high scores to the chat with the game, add the :paramref:`~telegram.Bot.set_game_score.disable_edit_message` parameter to disable automatic update of the message with the current scoreboard.
* Use :meth:`~telegram.Bot.getGameHighScores` to get data for in-game high score tables.
* You can also add an extra sharing button for users to share their best score to different chats.
* For examples of what can be done using this new stuff, check the `@gamebot <https://t.me/gamebot>`_ and `@gamee <https://t.me/gamee>`_ bots.
.. toctree::
:titlesonly:
+8
View File
@@ -1,6 +1,14 @@
.. _inline-tree:
Inline Mode
-----------
The following methods and objects allow your bot to work in `inline mode <https://core.telegram.org/bots/inline>`_.
Please see Telegrams `Introduction to Inline bots <https://core.telegram.org/bots/inline>`_ for more details.
To enable this option, send the ``/setinline`` command to `@BotFather <https://t.me/botfather>`_ and provide the placeholder text that the user will see in the input field after typing your bot's name.
.. toctree::
:titlesonly:
@@ -0,0 +1,6 @@
PaidMediaPurchased
==================
.. autoclass:: telegram.PaidMediaPurchased
:members:
:show-inheritance:
+3
View File
@@ -1,6 +1,9 @@
Passport
--------
Passport is a unified authorization method for services that require personal identification. Users can upload their documents once, then instantly share their data with services that require real-world ID (finance, ICOs, etc.). Please see the `manual <https://core.telegram.org/passport>`_ for details.
.. toctree::
:titlesonly:
+5
View File
@@ -1,6 +1,11 @@
.. _payments-tree:
Payments
--------
Your bot can accept payments from Telegram users. Please see the `introduction to payments <https://core.telegram.org/bots/payments>`_ for more details on the process and how to set up payments for your bot.
.. toctree::
:titlesonly:
@@ -0,0 +1,6 @@
ReactionTypePaid
================
.. autoclass:: telegram.ReactionTypePaid
:members:
:show-inheritance:
+2
View File
@@ -1,6 +1,8 @@
Stickers
--------
The following methods and objects allow your bot to handle stickers and sticker sets.
.. toctree::
:titlesonly:
+3 -1
View File
@@ -60,10 +60,12 @@
.. |removed_thumb_note| replace:: Removed the deprecated argument and attribute ``thumb``.
.. |removed_thumb_url_note| replace:: Removed the deprecated argument and attribute ``thumb_url``.
.. |removed_thumb_url_note| replace:: Removed the deprecated argument and attribute ``thumb_url`` which made thumbnail_url mandatory.
.. |removed_thumb_wildcard_note| replace:: Removed the deprecated arguments and attributes ``thumb_*``.
.. |thumbnail_url_mandatory| replace:: Removal of the deprecated argument ``thumb_url`` made ``thumbnail_url`` mandatory.
.. |async_context_manager| replace:: Asynchronous context manager which
.. |reply_parameters| replace:: Description of the message to reply to.
+2 -2
View File
@@ -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)],
},
+12 -5
View File
@@ -67,7 +67,7 @@ all = [
]
callback-data = [
# Cachetools doesn't have a strict stability policy. Let's be cautious for now.
"cachetools~=5.3.3",
"cachetools>=5.3.3,<5.6.0",
]
ext = [
"python-telegram-bot[callback-data,job-queue,rate-limiter,webhooks]",
@@ -128,14 +128,21 @@ explicit-preview-rules = true # TODO: Drop this when RUF022 and RUF023 are out
ignore = ["PLR2004", "PLR0911", "PLR0912", "PLR0913", "PLR0915", "PERF203"]
select = ["E", "F", "I", "PL", "UP", "RUF", "PTH", "C4", "B", "PIE", "SIM", "RET", "RSE",
"G", "ISC", "PT", "ASYNC", "TCH", "SLOT", "PERF", "PYI", "FLY", "AIR", "RUF022",
"RUF023", "Q", "INP", "W", "YTT", "DTZ", "ARG", "T20", "FURB"]
"RUF023", "Q", "INP", "W", "YTT", "DTZ", "ARG", "T20", "FURB", "DOC", "TRY",
"D100", "D101", "D102", "D103", "D300", "D418", "D419", "S"]
# Add "A (flake8-builtins)" after we drop pylint
[tool.ruff.lint.per-file-ignores]
"tests/*.py" = ["B018"]
"tests/**.py" = ["RUF012", "ASYNC230", "DTZ", "ARG", "T201"]
"docs/**.py" = ["INP001", "ARG"]
"examples/**.py" = ["ARG"]
"tests/**.py" = ["RUF012", "ASYNC230", "DTZ", "ARG", "T201", "ASYNC109", "D", "S", "TRY"]
"telegram/**.py" = ["TRY003"]
"telegram/ext/_applicationbuilder.py" = ["TRY004"]
"telegram/ext/filters.py" = ["D102"]
"docs/**.py" = ["INP001", "ARG", "D", "TRY003", "S"]
"examples/**.py" = ["ARG", "D", "S105", "TRY003"]
[tool.ruff.lint.pydocstyle]
convention = "google"
# PYLINT:
[tool.pylint."messages control"]
+1 -1
View File
@@ -4,7 +4,7 @@
build
# For the test suite
pytest==8.2.2
pytest==8.3.3
# needed because pytest doesn't come with native support for coroutines as tests
pytest-asyncio==0.21.2
+17 -2
View File
@@ -180,6 +180,7 @@ __all__ = (
"PaidMediaInfo",
"PaidMediaPhoto",
"PaidMediaPreview",
"PaidMediaPurchased",
"PaidMediaVideo",
"PassportData",
"PassportElementError",
@@ -204,6 +205,7 @@ __all__ = (
"ReactionType",
"ReactionTypeCustomEmoji",
"ReactionTypeEmoji",
"ReactionTypePaid",
"RefundedPayment",
"ReplyKeyboardMarkup",
"ReplyKeyboardRemove",
@@ -418,7 +420,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,
@@ -467,7 +476,13 @@ from ._payment.stars import (
from ._payment.successfulpayment import SuccessfulPayment
from ._poll import InputPollOption, Poll, PollAnswer, PollOption
from ._proximityalerttriggered import ProximityAlertTriggered
from ._reaction import ReactionCount, ReactionType, ReactionTypeCustomEmoji, ReactionTypeEmoji
from ._reaction import (
ReactionCount,
ReactionType,
ReactionTypeCustomEmoji,
ReactionTypeEmoji,
ReactionTypePaid,
)
from ._reply import ExternalReplyInfo, ReplyParameters, TextQuote
from ._replykeyboardmarkup import ReplyKeyboardMarkup
from ._replykeyboardremove import ReplyKeyboardRemove
+1 -1
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/].
# pylint: disable=missing-module-docstring
# ruff: noqa: T201
# ruff: noqa: T201, D100, S603, S607
import subprocess
import sys
from typing import Optional
+200 -58
View File
@@ -18,6 +18,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 Bot."""
import asyncio
import contextlib
import copy
@@ -324,10 +325,10 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
"""
try:
await self.initialize()
return self
except Exception as exc:
except Exception:
await self.shutdown()
raise exc
raise
return self
async def __aexit__(
self,
@@ -1305,8 +1306,8 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
photo (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.PhotoSize`): Photo to send.
photo (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` \
| :class:`pathlib.Path` | :class:`telegram.PhotoSize`): Photo to send.
|fileinput|
Lastly you can pass an existing :class:`telegram.PhotoSize` object to send.
@@ -1465,9 +1466,9 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
audio (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Audio`): Audio file to send.
|fileinput|
audio (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | \
:obj:`bytes` | :class:`pathlib.Path` | :class:`telegram.Audio`): Audio file to
send. |fileinput|
Lastly you can pass an existing :class:`telegram.Audio` object to send.
.. versionchanged:: 13.2
@@ -1617,8 +1618,8 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
document (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Document`): File to send.
document (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | \
:obj:`bytes` | :class:`pathlib.Path` | :class:`telegram.Document`): File to send.
|fileinput|
Lastly you can pass an existing :class:`telegram.Document` object to send.
@@ -1755,8 +1756,8 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Sticker`): Sticker to send.
sticker (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | \
:obj:`bytes` | :class:`pathlib.Path` | :class:`telegram.Sticker`): Sticker to send.
|fileinput| Video stickers can only be sent by a ``file_id``. Video and animated
stickers can't be sent via an HTTP URL.
@@ -1895,8 +1896,8 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
video (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Video`): Video file to send.
video (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` \
| :class:`pathlib.Path` | :class:`telegram.Video`): Video file to send.
|fileinput|
Lastly you can pass an existing :class:`telegram.Video` object to send.
@@ -2059,8 +2060,9 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
video_note (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.VideoNote`): Video note to send.
video_note (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | \
:obj:`bytes` | :class:`pathlib.Path` | :class:`telegram.VideoNote`): Video note
to send.
Pass a file_id as String to send a video note that exists on the Telegram
servers (recommended) or upload a new video using multipart/form-data.
|uploadinput|
@@ -2209,9 +2211,9 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
animation (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Animation`): Animation to send.
|fileinput|
animation (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | \
:obj:`bytes` | :class:`pathlib.Path` | :class:`telegram.Animation`): Animation to
send. |fileinput|
Lastly you can pass an existing :class:`telegram.Animation` object to send.
.. versionchanged:: 13.2
@@ -2371,8 +2373,8 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
voice (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Voice`): Voice file to send.
voice (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` \
| :class:`pathlib.Path` | :class:`telegram.Voice`): Voice file to send.
|fileinput|
Lastly you can pass an existing :class:`telegram.Voice` object to send.
@@ -4270,7 +4272,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
self,
offset: Optional[int] = None,
limit: Optional[int] = None,
timeout: Optional[int] = None,
timeout: Optional[int] = None, # noqa: ASYNC109
allowed_updates: Optional[Sequence[str]] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -4386,7 +4388,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
self._LOGGER.critical(
"Error while parsing updates! Received data was %r", result, exc_info=exc
)
raise exc
raise
async def set_webhook(
self,
@@ -4426,18 +4428,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
If you're having any trouble setting up webhooks, please check out this `guide to
Webhooks`_.
Note:
1. You will not be able to receive updates using :meth:`get_updates` for long as an
outgoing webhook is set up.
2. To use a self-signed certificate, you need to upload your public key certificate
using certificate parameter. Please upload as InputFile, sending a String will not
work.
3. Ports currently supported for Webhooks:
:attr:`telegram.constants.SUPPORTED_WEBHOOK_PORTS`.
If you're having any trouble setting up webhooks, please check out this `guide to
Webhooks`_.
.. seealso:: :meth:`telegram.ext.Application.run_webhook`,
:meth:`telegram.ext.Updater.start_webhook`
@@ -5018,7 +5008,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
payload (:obj:`str`): Bot-defined invoice payload.
:tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`-
:tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be
displayed to the user, use for your internal processes.
displayed to the user, use it for your internal processes.
provider_token (:obj:`str`): Payments provider token, obtained via
`@BotFather <https://t.me/BotFather>`_. Pass an empty string for payments in
|tg_stars|.
@@ -5784,10 +5774,10 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
invite_link (:obj:`str` | :obj:`telegram.ChatInviteLink`): The invite link to edit.
invite_link (:obj:`str` | :class:`telegram.ChatInviteLink`): The invite link to edit.
.. versionchanged:: 20.0
Now also accepts :obj:`telegram.ChatInviteLink` instances.
Now also accepts :class:`telegram.ChatInviteLink` instances.
expire_date (:obj:`int` | :obj:`datetime.datetime`, optional): Date when the link will
expire.
For timezone naive :obj:`datetime.datetime` objects, the default timezone of the
@@ -5856,10 +5846,10 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
invite_link (:obj:`str` | :obj:`telegram.ChatInviteLink`): The invite link to revoke.
invite_link (:obj:`str` | :class:`telegram.ChatInviteLink`): The invite link to revoke.
.. versionchanged:: 20.0
Now also accepts :obj:`telegram.ChatInviteLink` instances.
Now also accepts :class:`telegram.ChatInviteLink` instances.
Returns:
:class:`telegram.ChatInviteLink`
@@ -6131,6 +6121,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
chat_id: Union[str, int],
message_id: int,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
business_connection_id: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -6151,6 +6142,10 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
disable_notification (:obj:`bool`, optional): Pass :obj:`True`, if it is not necessary
to send a notification to all chat members about the new pinned message.
Notifications are always disabled in channels and private chats.
business_connection_id (:obj:`str`, optional): Unique identifier of the business
connection on behalf of which the message will be pinned.
.. versionadded:: 21.5
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
@@ -6163,6 +6158,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
"chat_id": chat_id,
"message_id": message_id,
"disable_notification": disable_notification,
"business_connection_id": business_connection_id,
}
return await self._post(
@@ -6179,6 +6175,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
self,
chat_id: Union[str, int],
message_id: Optional[int] = None,
business_connection_id: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -6195,8 +6192,13 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
message_id (:obj:`int`, optional): Identifier of a message to unpin. If not specified,
message_id (:obj:`int`, optional): Identifier of the message to unpin. Required if
:paramref:`business_connection_id` is specified. If not specified,
the most recent pinned message (by sending date) will be unpinned.
business_connection_id (:obj:`str`, optional): Unique identifier of the business
connection on behalf of which the message will be unpinned.
.. versionadded:: 21.5
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
@@ -6205,7 +6207,11 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
:class:`telegram.error.TelegramError`
"""
data: JSONDict = {"chat_id": chat_id, "message_id": message_id}
data: JSONDict = {
"chat_id": chat_id,
"message_id": message_id,
"business_connection_id": business_connection_id,
}
return await self._post(
"unpinChatMessage",
@@ -6354,8 +6360,9 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
Args:
user_id (:obj:`int`): User identifier of sticker file owner.
sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`):
A file with the sticker in the ``".WEBP"``, ``".PNG"``, ``".TGS"`` or ``".WEBM"``
sticker (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | \
:obj:`bytes` | :class:`pathlib.Path`): A file with the sticker in the
``".WEBP"``, ``".PNG"``, ``".TGS"`` or ``".WEBM"``
format. See `here <https://core.telegram.org/stickers>`_ for technical requirements
. |uploadinput|
@@ -6679,8 +6686,9 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
.. versionadded:: 21.1
thumbnail (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`, \
optional): A **.WEBP** or **.PNG** image with the thumbnail, must
thumbnail (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | \
:obj:`bytes` | :class:`pathlib.Path`, optional): A **.WEBP** or **.PNG** image
with the thumbnail, must
be up to :tg-const:`telegram.constants.StickerSetLimit.MAX_STATIC_THUMBNAIL_SIZE`
kilobytes in size and have width and height of exactly
:tg-const:`telegram.constants.StickerSetLimit.STATIC_THUMB_DIMENSIONS` px, or a
@@ -7383,8 +7391,9 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
.. versionadded:: 20.0
Args:
rights (:obj:`telegram.ChatAdministratorRights`, optional): A
:obj:`telegram.ChatAdministratorRights` object describing new default administrator
rights (:class:`telegram.ChatAdministratorRights`, optional): A
:class:`telegram.ChatAdministratorRights` object describing new default
administrator
rights. If not specified, the default administrator rights will be cleared.
for_channels (:obj:`bool`, optional): Pass :obj:`True` to change the default
administrator rights of the bot in channels. Otherwise, the default administrator
@@ -7394,7 +7403,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
:obj:`bool`: Returns :obj:`True` on success.
Raises:
:obj:`telegram.error.TelegramError`
:exc:`telegram.error.TelegramError`
"""
data: JSONDict = {"rights": rights, "for_channels": for_channels}
@@ -7960,7 +7969,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
payload (:obj:`str`): Bot-defined invoice payload.
:tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`-
:tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be
displayed to the user, use for your internal processes.
displayed to the user, use it for your internal processes.
provider_token (:obj:`str`): Payments provider token, obtained via
`@BotFather <https://t.me/BotFather>`_. Pass an empty string for payments in
|tg_stars|.
@@ -8160,7 +8169,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
) -> bool:
"""
Use this method to edit name and icon of a topic in a forum supergroup chat. The bot must
be an administrator in the chat for this to work and must have
be an administrator in the chat for this to work and must have the
:paramref:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights,
unless it is the creator of the topic.
@@ -8428,7 +8437,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
) -> bool:
"""
Use this method to edit the name of the 'General' topic in a forum supergroup chat. The bot
must be an administrator in the chat for this to work and must have
must be an administrator in the chat for this to work and must have the
:attr:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights.
.. versionadded:: 20.0
@@ -8927,7 +8936,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
"""
Use this method to change the chosen reactions on a message. Service messages can't be
reacted to. Automatically forwarded messages from a channel to its discussion group have
the same available reactions as messages in the channel.
the same available reactions as messages in the channel. Bots can't use paid reactions.
.. versionadded:: 20.8
@@ -8940,7 +8949,8 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
:class:`telegram.ReactionType` | :obj:`str`, optional): A list of reaction
types to set on the message. Currently, as non-premium users, bots can set up to
one reaction per message. A custom emoji reaction can be used if it is either
already present on the message or explicitly allowed by chat administrators.
already present on the message or explicitly allowed by chat administrators. Paid
reactions can't be used by bots.
Tip:
Passed :obj:`str` values will be converted to either
@@ -9057,7 +9067,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
user_id (:obj:`int`): User identifier of the sticker set owner.
name (:obj:`str`): Sticker set name.
old_sticker (:obj:`str`): File identifier of the replaced sticker.
sticker (:obj:`telegram.InputSticker`): An object with information about the added
sticker (:class:`telegram.InputSticker`): An object with information about the added
sticker. If exactly the same sticker had already been added to the set, then the
set remains unchanged.
@@ -9182,6 +9192,8 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
protect_content: ODVInput[bool] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
reply_markup: Optional[ReplyMarkup] = None,
business_connection_id: Optional[str] = None,
payload: Optional[str] = None,
*,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
@@ -9191,16 +9203,24 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> Message:
"""Use this method to send paid media to channel chats.
"""Use this method to send paid media.
.. versionadded:: 21.4
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| If the chat is a channel, all
Telegram Star proceeds from this media will be credited to the chat's balance.
Otherwise, they will be credited to the bot's balance.
star_count (:obj:`int`): The number of Telegram Stars that must be paid to buy access
to the media.
to the media; :tg-const:`telegram.constants.InvoiceLimit.MIN_STAR_COUNT` -
:tg-const:`telegram.constants.InvoiceLimit.MAX_STAR_COUNT`.
media (Sequence[:class:`telegram.InputPaidMedia`]): A list describing the media to be
sent; up to :tg-const:`telegram.constants.MediaGroupLimit.MAX_MEDIA_LENGTH` items.
payload (:obj:`str`, optional): Bot-defined paid media payload,
0-:tg-const:`telegram.constants.InvoiceLimit.MAX_PAYLOAD_LENGTH` bytes. This will
not be displayed to the user, use it for your internal processes.
.. versionadded:: 21.6
caption (:obj:`str`, optional): Caption of the media to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters.
parse_mode (:obj:`str`, optional): |parse_mode|
@@ -9214,6 +9234,9 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
:class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional):
Additional interface options. An object for an inline keyboard, custom reply
keyboard, instructions to remove reply keyboard or to force a reply from the user.
business_connection_id (:obj:`str`, optional): |business_id_str|
.. versionadded:: 21.5
Keyword Args:
allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply|
@@ -9236,6 +9259,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
"star_count": star_count,
"media": media,
"show_caption_above_media": show_caption_above_media,
"payload": payload,
}
return await self._send_message(
@@ -9255,8 +9279,122 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
business_connection_id=business_connection_id,
)
async def create_chat_subscription_invite_link(
self,
chat_id: Union[str, int],
subscription_period: int,
subscription_price: int,
name: Optional[str] = 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,
) -> ChatInviteLink:
"""
Use this method to create a `subscription invite link <https://telegram.org/blog/\
superchannels-star-reactions-subscriptions#star-subscriptions>`_ for a channel chat.
The bot must have the :attr:`~telegram.ChatPermissions.can_invite_users` administrator
right. The link can be edited using the :meth:`edit_chat_subscription_invite_link` or
revoked using the :meth:`revoke_chat_invite_link`.
.. versionadded:: 21.5
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
subscription_period (:obj:`int`): The number of seconds the subscription will be
active for before the next payment. Currently, it must always be
:tg-const:`telegram.constants.ChatSubscriptionLimit.SUBSCRIPTION_PERIOD` (30 days).
subscription_price (:obj:`int`): The number of Telegram Stars a user must pay initially
and after each subsequent subscription period to be a member of the chat;
:tg-const:`telegram.constants.ChatSubscriptionLimit.MIN_PRICE`-
:tg-const:`telegram.constants.ChatSubscriptionLimit.MAX_PRICE`.
name (:obj:`str`, optional): Invite link name;
0-:tg-const:`telegram.constants.ChatInviteLinkLimit.NAME_LENGTH` characters.
Returns:
:class:`telegram.ChatInviteLink`
Raises:
:class:`telegram.error.TelegramError`
"""
data: JSONDict = {
"chat_id": chat_id,
"subscription_period": subscription_period,
"subscription_price": subscription_price,
"name": name,
}
result = await self._post(
"createChatSubscriptionInviteLink",
data,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
)
return ChatInviteLink.de_json(result, self) # type: ignore[return-value]
async def edit_chat_subscription_invite_link(
self,
chat_id: Union[str, int],
invite_link: Union[str, "ChatInviteLink"],
name: Optional[str] = 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,
) -> ChatInviteLink:
"""
Use this method to edit a subscription invite link created by the bot. The bot must have
:attr:`telegram.ChatPermissions.can_invite_users` administrator right.
.. versionadded:: 21.5
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
invite_link (:obj:`str` | :obj:`telegram.ChatInviteLink`): The invite link to edit.
name (:obj:`str`, optional): Invite link name;
0-:tg-const:`telegram.constants.ChatInviteLinkLimit.NAME_LENGTH` characters.
Tip:
Omitting this argument removes the name of the invite link.
Returns:
:class:`telegram.ChatInviteLink`
Raises:
:class:`telegram.error.TelegramError`
"""
link = invite_link.invite_link if isinstance(invite_link, ChatInviteLink) else invite_link
data: JSONDict = {
"chat_id": chat_id,
"invite_link": link,
"name": name,
}
result = await self._post(
"editChatSubscriptionInviteLink",
data,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
)
return ChatInviteLink.de_json(result, self) # type: ignore[return-value]
def to_dict(self, recursive: bool = True) -> JSONDict: # noqa: ARG002
"""See :meth:`telegram.TelegramObject.to_dict`."""
data: JSONDict = {"id": self.id, "username": self.username, "first_name": self.first_name}
@@ -9513,3 +9651,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
"""Alias for :meth:`get_star_transactions`"""
sendPaidMedia = send_paid_media
"""Alias for :meth:`send_paid_media`"""
createChatSubscriptionInviteLink = create_chat_subscription_invite_link
"""Alias for :meth:`create_chat_subscription_invite_link`"""
editChatSubscriptionInviteLink = edit_chat_subscription_invite_link
"""Alias for :meth:`edit_chat_subscription_invite_link`"""
+83
View File
@@ -905,6 +905,7 @@ class _ChatBase(TelegramObject):
self,
message_id: int,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
business_connection_id: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -932,11 +933,13 @@ class _ChatBase(TelegramObject):
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
business_connection_id=business_connection_id,
)
async def unpin_message(
self,
message_id: Optional[int] = None,
business_connection_id: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -963,6 +966,7 @@ class _ChatBase(TelegramObject):
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
message_id=message_id,
business_connection_id=business_connection_id,
)
async def unpin_all_messages(
@@ -2662,6 +2666,81 @@ class _ChatBase(TelegramObject):
api_kwargs=api_kwargs,
)
async def create_subscription_invite_link(
self,
subscription_period: int,
subscription_price: int,
name: Optional[str] = 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,
) -> "ChatInviteLink":
"""Shortcut for::
await bot.create_chat_subscription_invite_link(
chat_id=update.effective_chat.id, *args, **kwargs
)
For the documentation of the arguments, please see
:meth:`telegram.Bot.create_chat_subscription_invite_link`.
.. versionadded:: 21.5
Returns:
:class:`telegram.ChatInviteLink`
"""
return await self.get_bot().create_chat_subscription_invite_link(
chat_id=self.id,
subscription_period=subscription_period,
subscription_price=subscription_price,
name=name,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
)
async def edit_subscription_invite_link(
self,
invite_link: Union[str, "ChatInviteLink"],
name: Optional[str] = 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,
) -> "ChatInviteLink":
"""Shortcut for::
await bot.edit_chat_subscription_invite_link(
chat_id=update.effective_chat.id, *args, **kwargs
)
For the documentation of the arguments, please see
:meth:`telegram.Bot.edit_chat_subscription_invite_link`.
.. versionadded:: 21.5
Returns:
:class:`telegram.ChatInviteLink`
"""
return await self.get_bot().edit_chat_subscription_invite_link(
chat_id=self.id,
invite_link=invite_link,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
name=name,
)
async def approve_join_request(
self,
user_id: int,
@@ -3270,6 +3349,8 @@ class _ChatBase(TelegramObject):
protect_content: ODVInput[bool] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
reply_markup: Optional[ReplyMarkup] = None,
business_connection_id: Optional[str] = None,
payload: Optional[str] = None,
*,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
@@ -3310,6 +3391,8 @@ class _ChatBase(TelegramObject):
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
business_connection_id=business_connection_id,
payload=payload,
)
+10 -10
View File
@@ -307,7 +307,7 @@ class BackgroundTypeFill(BackgroundType):
.. versionadded:: 21.2
Args:
fill (:obj:`telegram.BackgroundFill`): The background fill.
fill (:class:`telegram.BackgroundFill`): The background fill.
dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a
percentage;
0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`.
@@ -315,7 +315,7 @@ class BackgroundTypeFill(BackgroundType):
Attributes:
type (:obj:`str`): Type of the background. Always
:attr:`~telegram.BackgroundType.FILL`.
fill (:obj:`telegram.BackgroundFill`): The background fill.
fill (:class:`telegram.BackgroundFill`): The background fill.
dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a
percentage;
0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`.
@@ -349,7 +349,7 @@ class BackgroundTypeWallpaper(BackgroundType):
.. versionadded:: 21.2
Args:
document (:obj:`telegram.Document`): Document with the wallpaper
document (:class:`telegram.Document`): Document with the wallpaper
dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a
percentage;
0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`.
@@ -361,7 +361,7 @@ class BackgroundTypeWallpaper(BackgroundType):
Attributes:
type (:obj:`str`): Type of the background. Always
:attr:`~telegram.BackgroundType.WALLPAPER`.
document (:obj:`telegram.Document`): Document with the wallpaper
document (:class:`telegram.Document`): Document with the wallpaper
dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a
percentage;
0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`.
@@ -407,8 +407,8 @@ class BackgroundTypePattern(BackgroundType):
.. versionadded:: 21.2
Args:
document (:obj:`telegram.Document`): Document with the pattern.
fill (:obj:`telegram.BackgroundFill`): The background fill that is combined with
document (:class:`telegram.Document`): Document with the pattern.
fill (:class:`telegram.BackgroundFill`): The background fill that is combined with
the pattern.
intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled
background;
@@ -422,8 +422,8 @@ class BackgroundTypePattern(BackgroundType):
Attributes:
type (:obj:`str`): Type of the background. Always
:attr:`~telegram.BackgroundType.PATTERN`.
document (:obj:`telegram.Document`): Document with the pattern.
fill (:obj:`telegram.BackgroundFill`): The background fill that is combined with
document (:class:`telegram.Document`): Document with the pattern.
fill (:class:`telegram.BackgroundFill`): The background fill that is combined with
the pattern.
intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled
background;
@@ -511,10 +511,10 @@ class ChatBackground(TelegramObject):
.. versionadded:: 21.2
Args:
type (:obj:`telegram.BackgroundType`): Type of the background.
type (:class:`telegram.BackgroundType`): Type of the background.
Attributes:
type (:obj:`telegram.BackgroundType`): Type of the background.
type (:class:`telegram.BackgroundType`): Type of the background.
"""
__slots__ = ("type",)
+17 -4
View File
@@ -187,15 +187,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 +212,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 +236,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
+6 -6
View File
@@ -78,7 +78,7 @@ class ChatFullInfo(_ChatBase):
#collectible-usernames>`_; for private chats, supergroups and channels.
.. versionadded:: 20.0
birthdate (:obj:`telegram.Birthdate`, optional): For private chats,
birthdate (:class:`telegram.Birthdate`, optional): For private chats,
the date of birth of the user.
.. versionadded:: 21.1
@@ -94,8 +94,8 @@ class ChatFullInfo(_ChatBase):
chats with business accounts, the opening hours of the business.
.. versionadded:: 21.1
personal_chat (:obj:`telegram.Chat`, optional): For private chats, the personal channel of
the user.
personal_chat (:class:`telegram.Chat`, optional): For private chats, the personal channel
of the user.
.. versionadded:: 21.1
available_reactions (Sequence[:class:`telegram.ReactionType`], optional): List of available
@@ -232,7 +232,7 @@ class ChatFullInfo(_ChatBase):
obtained via :meth:`~telegram.Bot.get_chat`.
.. versionadded:: 20.0
birthdate (:obj:`telegram.Birthdate`): Optional. For private chats,
birthdate (:class:`telegram.Birthdate`): Optional. For private chats,
the date of birth of the user.
.. versionadded:: 21.1
@@ -248,8 +248,8 @@ class ChatFullInfo(_ChatBase):
chats with business accounts, the opening hours of the business.
.. versionadded:: 21.1
personal_chat (:obj:`telegram.Chat`): Optional. For private chats, the personal channel of
the user.
personal_chat (:class:`telegram.Chat`): Optional. For private chats, the personal channel
of the user.
.. versionadded:: 21.1
available_reactions (Tuple[:class:`telegram.ReactionType`]): Optional. List of available
+26
View File
@@ -69,6 +69,16 @@ class ChatInviteLink(TelegramObject):
created using this link.
.. versionadded:: 13.8
subscription_period (:obj:`int`, optional): The number of seconds the subscription will be
active for before the next payment.
.. versionadded:: 21.5
subscription_price (:obj:`int`, optional): The amount of Telegram Stars a user must pay
initially and after each subsequent subscription period to be a member of the chat
using the link.
.. versionadded:: 21.5
Attributes:
invite_link (:obj:`str`): The invite link. If the link was created by another chat
administrator, then the second part of the link will be replaced with ``''``.
@@ -96,6 +106,15 @@ class ChatInviteLink(TelegramObject):
created using this link.
.. versionadded:: 13.8
subscription_period (:obj:`int`): Optional. The number of seconds the subscription will be
active for before the next payment.
.. versionadded:: 21.5
subscription_price (:obj:`int`): Optional. The amount of Telegram Stars a user must pay
initially and after each subsequent subscription period to be a member of the chat
using the link.
.. versionadded:: 21.5
"""
@@ -109,6 +128,8 @@ class ChatInviteLink(TelegramObject):
"member_limit",
"name",
"pending_join_request_count",
"subscription_period",
"subscription_price",
)
def __init__(
@@ -122,6 +143,8 @@ class ChatInviteLink(TelegramObject):
member_limit: Optional[int] = None,
name: Optional[str] = None,
pending_join_request_count: Optional[int] = None,
subscription_period: Optional[int] = None,
subscription_price: Optional[int] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
@@ -140,6 +163,9 @@ class ChatInviteLink(TelegramObject):
self.pending_join_request_count: Optional[int] = (
int(pending_join_request_count) if pending_join_request_count is not None else None
)
self.subscription_period: Optional[int] = subscription_period
self.subscription_price: Optional[int] = subscription_price
self._id_attrs = (
self.invite_link,
self.creates_join_request,
+13 -2
View File
@@ -17,6 +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 ChatMember."""
import datetime
from typing import TYPE_CHECKING, Dict, Final, Optional, Type
@@ -391,24 +392,34 @@ class ChatMemberMember(ChatMember):
Args:
user (:class:`telegram.User`): Information about the user.
until_date (:class:`datetime.datetime`, optional): Date when the user's subscription will
expire.
.. versionadded:: 21.5
Attributes:
status (:obj:`str`): The member's status in the chat,
always :tg-const:`telegram.ChatMember.MEMBER`.
user (:class:`telegram.User`): Information about the user.
until_date (:class:`datetime.datetime`): Optional. Date when the user's subscription will
expire.
.. versionadded:: 21.5
"""
__slots__ = ()
__slots__ = ("until_date",)
def __init__(
self,
user: User,
until_date: Optional[datetime.datetime] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
super().__init__(status=ChatMember.MEMBER, user=user, api_kwargs=api_kwargs)
self._freeze()
with self._unfrozen():
self.until_date: Optional[datetime.datetime] = until_date
class ChatMemberRestricted(ChatMember):
+41 -6
View File
@@ -22,7 +22,8 @@ import mimetypes
from typing import IO, Optional, Union
from uuid import uuid4
from telegram._utils.files import load_file
from telegram._utils.files import guess_file_name, load_file
from telegram._utils.strings import TextEncoding
from telegram._utils.types import FieldTuple
_DEFAULT_MIME_TYPE = "application/octet-stream"
@@ -52,9 +53,36 @@ class InputFile:
attach (:obj:`bool`, optional): Pass :obj:`True` if the parameter this file belongs to in
the request to Telegram should point to the multipart data via an ``attach://`` URI.
Defaults to `False`.
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`.
Tip:
If you upload extremely large files, you may want to set this to :obj:`False` to
avoid reading the complete file into memory. Additionally, this may be supported
better by the networking backend (in particular it is handled better by
the default :class:`~telegram.request.HTTPXRequest`).
Important:
If you set this to :obj:`False`, you have to ensure that the file handle is still
open when the request is made. In particular, the following snippet can *not* work
as expected.
.. code-block:: python
with open('file.txt', 'rb') as file:
input_file = InputFile(file, read_file_handle=False)
# here the file handle is already closed and the upload will fail
await bot.send_document(chat_id, input_file)
.. versionadded:: 21.5
Attributes:
input_file_content (:obj:`bytes`): The binary content of the file to send.
input_file_content (:obj:`bytes` | :class:`IO`): The binary content of the file to send.
attach_name (:obj:`str`): Optional. If present, the parameter this file belongs to in
the request to Telegram should point to the multipart data via a an URI of the form
``attach://<attach_name>`` URI.
@@ -70,14 +98,18 @@ class InputFile:
obj: Union[IO[bytes], bytes, str],
filename: Optional[str] = None,
attach: bool = False,
read_file_handle: bool = True,
):
if isinstance(obj, bytes):
self.input_file_content: bytes = obj
self.input_file_content: Union[bytes, IO[bytes]] = obj
elif isinstance(obj, str):
self.input_file_content = obj.encode("utf-8")
else:
self.input_file_content = obj.encode(TextEncoding.UTF_8)
elif read_file_handle:
reported_filename, self.input_file_content = load_file(obj)
filename = filename or reported_filename
else:
self.input_file_content = obj
filename = filename or guess_file_name(obj)
self.attach_name: Optional[str] = "attached" + uuid4().hex if attach else None
@@ -94,8 +126,11 @@ class InputFile:
def field_tuple(self) -> FieldTuple:
"""Field tuple representing the contents of the file for upload to the Telegram servers.
.. versionchanged:: 21.5
Content may now be a file handle.
Returns:
Tuple[:obj:`str`, :obj:`bytes`, :obj:`str`]:
Tuple[:obj:`str`, :obj:`bytes` | :class:`IO`, :obj:`str`]:
"""
return self.filename, self.input_file_content, self.mimetype
+19 -18
View File
@@ -50,8 +50,8 @@ class InputMedia(TelegramObject):
Args:
media_type (:obj:`str`): Type of media that the instance represents.
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Animation` | :class:`telegram.Audio` | \
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path` | :class:`telegram.Animation` | :class:`telegram.Audio` | \
:class:`telegram.Document` | :class:`telegram.PhotoSize` | \
:class:`telegram.Video`): File to send.
|fileinputnopath|
@@ -128,8 +128,9 @@ class InputPaidMedia(TelegramObject):
Args:
type (:obj:`str`): Type of media that the instance represents.
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.PhotoSize` | :class:`telegram.Video`): File to send. |fileinputnopath|
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path` | :class:`telegram.PhotoSize` | :class:`telegram.Video`): File
to send. |fileinputnopath|
Lastly you can pass an existing telegram media object of the corresponding type
to send.
@@ -167,8 +168,8 @@ class InputPaidMediaPhoto(InputPaidMedia):
.. versionadded:: 21.4
Args:
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.PhotoSize`): File to send. |fileinputnopath|
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path` | :class:`telegram.PhotoSize`): File to send. |fileinputnopath|
Lastly you can pass an existing :class:`telegram.PhotoSize` object to send.
Attributes:
@@ -207,8 +208,8 @@ class InputPaidMediaVideo(InputPaidMedia):
changed by Telegram.
Args:
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Video`): File to send. |fileinputnopath|
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path` | :class:`telegram.Video`): File to send. |fileinputnopath|
Lastly you can pass an existing :class:`telegram.Video` object to send.
thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
optional): |thumbdocstringnopath|
@@ -278,8 +279,8 @@ class InputMediaAnimation(InputMedia):
|removed_thumb_note|
Args:
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Animation`): File to send. |fileinputnopath|
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path` | :class:`telegram.Animation`): File to send. |fileinputnopath|
Lastly you can pass an existing :class:`telegram.Animation` object to send.
.. versionchanged:: 13.2
@@ -401,8 +402,8 @@ class InputMediaPhoto(InputMedia):
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
Args:
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.PhotoSize`): File to send. |fileinputnopath|
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path` | :class:`telegram.PhotoSize`): File to send. |fileinputnopath|
Lastly you can pass an existing :class:`telegram.PhotoSize` object to send.
.. versionchanged:: 13.2
@@ -501,8 +502,8 @@ class InputMediaVideo(InputMedia):
|removed_thumb_note|
Args:
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Video`): File to send. |fileinputnopath|
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path` | :class:`telegram.Video`): File to send. |fileinputnopath|
Lastly you can pass an existing :class:`telegram.Video` object to send.
.. versionchanged:: 13.2
@@ -639,8 +640,8 @@ class InputMediaAudio(InputMedia):
|removed_thumb_note|
Args:
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Audio`): File to send. |fileinputnopath|
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` | \
:class:`pathlib.Path` | :class:`telegram.Audio`): File to send. |fileinputnopath|
Lastly you can pass an existing :class:`telegram.Audio` object to send.
.. versionchanged:: 13.2
@@ -743,8 +744,8 @@ class InputMediaDocument(InputMedia):
|removed_thumb_note|
Args:
media (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | \
:class:`telegram.Document`): File to send. |fileinputnopath|
media (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` \
| :class:`pathlib.Path` | :class:`telegram.Document`): File to send. |fileinputnopath|
Lastly you can pass an existing :class:`telegram.Document` object to send.
.. versionchanged:: 13.2
+4 -3
View File
@@ -41,14 +41,15 @@ class InputSticker(TelegramObject):
order of the arguments has changed.
Args:
sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`): The
sticker (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` \
| :class:`pathlib.Path`): The
added sticker. |uploadinputnopath| Animated and video stickers can't be uploaded via
HTTP URL.
emoji_list (Sequence[:obj:`str`]): Sequence of
:tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI` -
:tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with the
sticker.
mask_position (:obj:`telegram.MaskPosition`, optional): Position where the mask should be
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 (Sequence[:obj:`str`], optional): Sequence of
0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords
@@ -70,7 +71,7 @@ class InputSticker(TelegramObject):
:tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI` -
:tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with the
sticker.
mask_position (:obj:`telegram.MaskPosition`): Optional. Position where the mask should be
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
0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords
+3 -2
View File
@@ -24,6 +24,7 @@ from telegram._files.photosize import PhotoSize
from telegram._messageentity import MessageEntity
from telegram._telegramobject import TelegramObject
from telegram._utils.argumentparsing import parse_sequence_arg
from telegram._utils.strings import TextEncoding
from telegram._utils.types import JSONDict
if TYPE_CHECKING:
@@ -157,10 +158,10 @@ class Game(TelegramObject):
if not self.text:
raise RuntimeError("This Game has no 'text'.")
entity_text = self.text.encode("utf-16-le")
entity_text = self.text.encode(TextEncoding.UTF_16_LE)
entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2]
return entity_text.decode("utf-16-le")
return entity_text.decode(TextEncoding.UTF_16_LE)
def parse_text_entities(self, types: Optional[List[str]] = None) -> Dict[MessageEntity, str]:
"""
+57 -9
View File
@@ -56,8 +56,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
@@ -75,8 +80,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.
"""
__slots__ = (
@@ -86,6 +96,7 @@ class Giveaway(TelegramObject):
"only_new_members",
"premium_subscription_month_count",
"prize_description",
"prize_star_count",
"winner_count",
"winners_selection_date",
)
@@ -100,6 +111,7 @@ 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,
):
@@ -113,6 +125,7 @@ class Giveaway(TelegramObject):
self.prize_description: Optional[str] = prize_description
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,
@@ -126,7 +139,7 @@ class Giveaway(TelegramObject):
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["Giveaway"]:
"""See :obj:`telegram.TelegramObject.de_json`."""
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
if data is None:
@@ -145,13 +158,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 +201,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
@@ -194,6 +226,10 @@ class GiveawayWinners(TelegramObject):
: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 +247,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 +268,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,
):
@@ -247,6 +285,7 @@ class GiveawayWinners(TelegramObject):
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,
@@ -262,7 +301,7 @@ class GiveawayWinners(TelegramObject):
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["GiveawayWinners"]:
"""See :obj:`telegram.TelegramObject.de_json`."""
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
if data is None:
@@ -295,21 +334,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 +365,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,
@@ -330,7 +378,7 @@ class GiveawayCompleted(TelegramObject):
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["GiveawayCompleted"]:
"""See :obj:`telegram.TelegramObject.de_json`."""
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
if data is None:
+16 -20
View File
@@ -99,7 +99,7 @@ class InlineKeyboardButton(TelegramObject):
.. seealso:: :wiki:`Arbitrary callback_data <Arbitrary-callback_data>`
web_app (:obj:`telegram.WebAppInfo`, optional): Description of the `Web App
web_app (:class:`telegram.WebAppInfo`, optional): Description of the `Web App
<https://core.telegram.org/bots/webapps>`_ that will be launched when the user presses
the button. The Web App will be able to send an arbitrary message on behalf of the user
using the method :meth:`~telegram.Bot.answer_web_app_query`. Available only in
@@ -107,16 +107,14 @@ class InlineKeyboardButton(TelegramObject):
a Telegram Business account.
.. versionadded:: 20.0
switch_inline_query (:obj:`str`, optional): If set, pressing the button will insert the
bot's username and the specified inline query in the current chat's input field. May be
empty, in which case only the bot's username will be inserted.
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.
switch_inline_query (:obj:`str`, optional): If set, pressing the button will prompt the
user to select one of their chats, open that chat and insert the bot's username and the
specified inline query in the input field. May be empty, in which case just the bot's
username will be inserted. Not supported for messages sent on behalf of a Telegram
Business account.
Tip:
This is similar to the new parameter :paramref:`switch_inline_query_chosen_chat`,
This is similar to the parameter :paramref:`switch_inline_query_chosen_chat`,
but gives no control over which chats can be selected.
switch_inline_query_current_chat (:obj:`str`, optional): If set, pressing the button will
insert the bot's username and the specified inline query in the current chat's input
@@ -137,7 +135,7 @@ class InlineKeyboardButton(TelegramObject):
Note:
This type of button **must** always be the first button in the first row and can
only be used in invoice messages.
switch_inline_query_chosen_chat (:obj:`telegram.SwitchInlineQueryChosenChat`, optional):
switch_inline_query_chosen_chat (:class:`telegram.SwitchInlineQueryChosenChat`, optional):
If set, pressing the button will prompt the user to select one of their chats of the
specified type, open that chat and insert the bot's username and the specified inline
query in the input field. Not supported for messages sent on behalf of a Telegram
@@ -170,7 +168,7 @@ class InlineKeyboardButton(TelegramObject):
to the bot when the button is pressed, UTF-8
:tg-const:`telegram.InlineKeyboardButton.MIN_CALLBACK_DATA`-
:tg-const:`telegram.InlineKeyboardButton.MAX_CALLBACK_DATA` bytes.
web_app (:obj:`telegram.WebAppInfo`): Optional. Description of the `Web App
web_app (:class:`telegram.WebAppInfo`): Optional. Description of the `Web App
<https://core.telegram.org/bots/webapps>`_ that will be launched when the user presses
the button. The Web App will be able to send an arbitrary message on behalf of the user
using the method :meth:`~telegram.Bot.answer_web_app_query`. Available only in
@@ -178,16 +176,14 @@ class InlineKeyboardButton(TelegramObject):
a Telegram Business account.
.. versionadded:: 20.0
switch_inline_query (:obj:`str`): Optional. If set, pressing the button will insert the
bot's username and the specified inline query in the current chat's input field. May be
empty, in which case only the bot's username will be inserted.
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.
switch_inline_query (:obj:`str`): Optional. If set, pressing the button will prompt the
user to select one of their chats, open that chat and insert the bot's username and the
specified inline query in the input field. May be empty, in which case just the bot's
username will be inserted. Not supported for messages sent on behalf of a Telegram
Business account.
Tip:
This is similar to the new parameter :paramref:`switch_inline_query_chosen_chat`,
This is similar to the parameter :paramref:`switch_inline_query_chosen_chat`,
but gives no control over which chats can be selected.
switch_inline_query_current_chat (:obj:`str`): Optional. If set, pressing the button will
insert the bot's username and the specified inline query in the current chat's input
@@ -208,7 +204,7 @@ class InlineKeyboardButton(TelegramObject):
Note:
This type of button **must** always be the first button in the first row and can
only be used in invoice messages.
switch_inline_query_chosen_chat (:obj:`telegram.SwitchInlineQueryChosenChat`): Optional.
switch_inline_query_chosen_chat (:class:`telegram.SwitchInlineQueryChosenChat`): Optional.
If set, pressing the button will prompt the user to select one of their chats of the
specified type, open that chat and insert the bot's username and the specified inline
query in the input field. Not supported for messages sent on behalf of a Telegram
+5 -11
View File
@@ -50,16 +50,14 @@ class InlineQueryResultGif(InlineQueryResult):
gif_width (:obj:`int`, optional): Width of the GIF.
gif_height (:obj:`int`, optional): Height of the GIF.
gif_duration (:obj:`int`, optional): Duration of the GIF in seconds.
thumbnail_url (:obj:`str`, optional): URL of the static (JPEG or GIF) or animated (MPEG4)
thumbnail_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4)
thumbnail for the result.
Warning:
The Bot API does **not** define this as an optional argument. It is formally
optional for backwards compatibility with the deprecated :paramref:`thumb_url`.
If you pass neither :paramref:`thumbnail_url` nor :paramref:`thumb_url`,
:class:`ValueError` will be raised.
.. versionadded:: 20.2
..versionchanged:: 20.5
|thumbnail_url_mandatory|
thumbnail_mime_type (:obj:`str`, optional): MIME type of the thumbnail, must be one of
``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``.
@@ -82,10 +80,6 @@ class InlineQueryResultGif(InlineQueryResult):
.. versionadded:: 21.3
Raises:
:class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is
supplied or if both are supplied and are not equal.
Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.GIF`.
id (:obj:`str`): Unique identifier for this result,
+5 -10
View File
@@ -51,16 +51,14 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
mpeg4_width (:obj:`int`, optional): Video width.
mpeg4_height (:obj:`int`, optional): Video height.
mpeg4_duration (:obj:`int`, optional): Video duration in seconds.
thumbnail_url (:obj:`str`, optional): URL of the static (JPEG or GIF) or animated (MPEG4)
thumbnail_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4)
thumbnail for the result.
Warning:
The Bot API does **not** define this as an optional argument. It is formally
optional for backwards compatibility with the deprecated :paramref:`thumb_url`.
If you pass neither :paramref:`thumbnail_url` nor :paramref:`thumb_url`,
:class:`ValueError` will be raised.
.. versionadded:: 20.2
..versionchanged:: 20.5
|thumbnail_url_mandatory|
thumbnail_mime_type (:obj:`str`, optional): MIME type of the thumbnail, must be one of
``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``.
@@ -83,9 +81,6 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med|
.. versionadded:: 21.3
Raises:
:class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is
supplied or if both are supplied and are not equal.
Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.MPEG4GIF`.
+5 -11
View File
@@ -48,15 +48,13 @@ class InlineQueryResultPhoto(InlineQueryResult):
:tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes.
photo_url (:obj:`str`): A valid URL of the photo. Photo must be in JPEG format. Photo size
must not exceed 5MB.
thumbnail_url (:obj:`str`, optional): URL of the thumbnail for the photo.
Warning:
The Bot API does **not** define this as an optional argument. It is formally
optional for backwards compatibility with the deprecated :paramref:`thumb_url`.
If you pass neither :paramref:`thumbnail_url` nor :paramref:`thumb_url`,
:class:`ValueError` will be raised.
thumbnail_url (:obj:`str`): URL of the thumbnail for the photo.
.. versionadded:: 20.2
..versionchanged:: 20.5
|thumbnail_url_mandatory|
photo_width (:obj:`int`, optional): Width of the photo.
photo_height (:obj:`int`, optional): Height of the photo.
title (:obj:`str`, optional): Title for the result.
@@ -78,10 +76,6 @@ class InlineQueryResultPhoto(InlineQueryResult):
.. versionadded:: 21.3
Raises:
:class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is
supplied or if both are supplied and are not equal.
Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.PHOTO`.
id (:obj:`str`): Unique identifier for this result,
+4 -17
View File
@@ -55,20 +55,12 @@ class InlineQueryResultVideo(InlineQueryResult):
mime_type (:obj:`str`): Mime type of the content of video url, "text/html" or "video/mp4".
thumbnail_url (:obj:`str`, optional): URL of the thumbnail (JPEG only) for the video.
Warning:
The Bot API does **not** define this as an optional argument. It is formally
optional for backwards compatibility with the deprecated :paramref:`thumb_url`.
If you pass neither :paramref:`thumbnail_url` nor :paramref:`thumb_url`,
:class:`ValueError` will be raised.
.. versionadded:: 20.2
title (:obj:`str`, optional): Title for the result.
Warning:
The Bot API does **not** define this as an optional argument. It is formally
optional to ensure backwards compatibility of :paramref:`thumbnail_url` with the
deprecated :paramref:`thumb_url`, which required that :paramref:`thumbnail_url`
become optional. :class:`TypeError` will be raised if no ``title`` is passed.
..versionchanged:: 20.5
|thumbnail_url_mandatory|
title (:obj:`str`): Title for the result.
caption (:obj:`str`, optional): Caption of the video to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities
parsing.
@@ -92,11 +84,6 @@ class InlineQueryResultVideo(InlineQueryResult):
.. versionadded:: 21.3
Raises:
:class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is
supplied or if both are supplied and are not equal.
:class:`TypeError`: If no :paramref:`title` is passed.
Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VIDEO`.
id (:obj:`str`): Unique identifier for this result,
@@ -47,7 +47,7 @@ class InputInvoiceMessageContent(InputMessageContent):
payload (:obj:`str`): Bot-defined invoice payload.
:tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`-
:tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be displayed
to the user, use for your internal processes.
to the user, use it for your internal processes.
provider_token (:obj:`str`): Payment provider token, obtained via
`@Botfather <https://t.me/Botfather>`_. Pass an empty string for payments in
|tg_stars|.
@@ -115,7 +115,7 @@ class InputInvoiceMessageContent(InputMessageContent):
payload (:obj:`str`): Bot-defined invoice payload.
:tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`-
:tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be displayed
to the user, use for your internal processes.
to the user, use it for your internal processes.
provider_token (:obj:`str`): Payment provider token, obtained via
`@Botfather <https://t.me/Botfather>`_. Pass an empty string for payments in `Telegram
Stars <https://t.me/BotNews/90>`_.
+61 -44
View File
@@ -68,6 +68,7 @@ from telegram._utils.argumentparsing import parse_sequence_arg
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue
from telegram._utils.entities import parse_message_entities, parse_message_entity
from telegram._utils.strings import TextEncoding
from telegram._utils.types import (
CorrectOptionID,
FileInput,
@@ -279,15 +280,14 @@ class Message(MaybeInaccessibleMessage):
Args:
message_id (:obj:`int`): Unique message identifier inside this chat.
from_user (:class:`telegram.User`, optional): Sender of the message; empty for messages
sent to channels. For backward compatibility, this will contain a fake sender user in
non-channel chats, if the message was sent on behalf of a chat.
sender_chat (:class:`telegram.Chat`, optional): Sender of the message, sent on behalf of a
chat. For example, the channel itself for channel posts, the supergroup itself for
messages from anonymous group administrators, the linked channel for messages
automatically forwarded to the discussion group. For backward compatibility,
:attr:`from_user` contains a fake sender user in non-channel chats, if the message was
sent on behalf of a chat.
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.
sender_chat (:class:`telegram.Chat`, optional): Sender of the message when sent on behalf
of a chat. For example, the supergroup itself for messages sent by its anonymous
administrators or a linked channel for messages automatically forwarded to the
channel's discussion group. For backward compatibility, if the message was sent on
behalf of a chat, the field from contains a fake sender user in non-channel chats.
date (:class:`datetime.datetime`): Date the message was sent in Unix time. Converted to
:class:`datetime.datetime`.
@@ -358,7 +358,7 @@ class Message(MaybeInaccessibleMessage):
about the animation. For backward compatibility, when this field is set, the document
field will also be set.
game (:class:`telegram.Game`, optional): Message is a game, information about the game.
`More about games >> <https://core.telegram.org/bots/api#games>`_.
:ref:`More about games >> <games-tree>`.
photo (Sequence[: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.
@@ -431,10 +431,10 @@ class Message(MaybeInaccessibleMessage):
:class:`telegram.InaccessibleMessage`.
invoice (:class:`telegram.Invoice`, optional): Message is an invoice for a payment,
information about the invoice.
`More about payments >> <https://core.telegram.org/bots/api#payments>`_.
:ref:`More about payments >> <payments-tree>`.
successful_payment (:class:`telegram.SuccessfulPayment`, optional): Message is a service
message about a successful payment, information about the payment.
`More about payments >> <https://core.telegram.org/bots/api#payments>`_.
:ref:`More about payments >> <payments-tree>`.
connected_website (:obj:`str`, optional): The domain name of the website on which the user
has logged in.
`More about Telegram Login >> <https://core.telegram.org/widgets/login>`_.
@@ -569,36 +569,35 @@ class Message(MaybeInaccessibleMessage):
.. versionadded:: 21.1
sender_business_bot (:obj:`telegram.User`, optional): The bot that actually sent the
sender_business_bot (:class:`telegram.User`, optional): The bot that actually sent the
message on behalf of the business account. Available only for outgoing messages sent
on behalf of the connected business account.
.. versionadded:: 21.1
chat_background_set (:obj:`telegram.ChatBackground`, optional): Service message: chat
chat_background_set (:class:`telegram.ChatBackground`, optional): Service message: chat
background set.
.. versionadded:: 21.2
paid_media (:obj:`telegram.PaidMediaInfo`, optional): Message contains paid media;
paid_media (:class:`telegram.PaidMediaInfo`, optional): Message contains paid media;
information about the paid media.
.. versionadded:: 21.4
refunded_payment (:obj:`telegram.RefundedPayment`, optional): Message is a service message
about a refunded payment, information about the payment.
refunded_payment (:class:`telegram.RefundedPayment`, optional): Message is a service
message about a refunded payment, information about the payment.
.. versionadded:: 21.4
Attributes:
message_id (:obj:`int`): Unique message identifier inside this chat.
from_user (:class:`telegram.User`): Optional. Sender of the message; empty for messages
sent to channels. For backward compatibility, this will contain a fake sender user in
non-channel chats, if the message was sent on behalf of a chat.
sender_chat (:class:`telegram.Chat`): Optional. Sender of the message, sent on behalf of a
chat. For example, the channel itself for channel posts, the supergroup itself for
messages from anonymous group administrators, the linked channel for messages
automatically forwarded to the discussion group. For backward compatibility,
:attr:`from_user` contains a fake sender user in non-channel chats, if the message was
sent on behalf of a chat.
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.
sender_chat (:class:`telegram.Chat`): Optional. Sender of the message when sent on behalf
of a chat. For example, the supergroup itself for messages sent by its anonymous
administrators or a linked channel for messages automatically forwarded to the
channel's discussion group. For backward compatibility, if the message was sent on
behalf of a chat, the field from contains a fake sender user in non-channel chats.
date (:class:`datetime.datetime`): Date the message was sent in Unix time. Converted to
:class:`datetime.datetime`.
@@ -675,7 +674,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.
`More about games >> <https://core.telegram.org/bots/api#games>`_.
:ref:`More about games >> <games-tree>`.
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.
@@ -757,10 +756,10 @@ class Message(MaybeInaccessibleMessage):
:class:`telegram.InaccessibleMessage`.
invoice (:class:`telegram.Invoice`): Optional. Message is an invoice for a payment,
information about the invoice.
`More about payments >> <https://core.telegram.org/bots/api#payments>`_.
:ref:`More about payments >> <payments-tree>`.
successful_payment (:class:`telegram.SuccessfulPayment`): Optional. Message is a service
message about a successful payment, information about the payment.
`More about payments >> <https://core.telegram.org/bots/api#payments>`_.
:ref:`More about payments >> <payments-tree>`.
connected_website (:obj:`str`): Optional. The domain name of the website on which the user
has logged in.
`More about Telegram Login >> <https://core.telegram.org/widgets/login>`_.
@@ -896,22 +895,22 @@ class Message(MaybeInaccessibleMessage):
.. versionadded:: 21.1
sender_business_bot (:obj:`telegram.User`): Optional. The bot that actually sent the
sender_business_bot (:class:`telegram.User`): Optional. The bot that actually sent the
message on behalf of the business account. Available only for outgoing messages sent
on behalf of the connected business account.
.. versionadded:: 21.1
chat_background_set (:obj:`telegram.ChatBackground`): Optional. Service message: chat
chat_background_set (:class:`telegram.ChatBackground`): Optional. Service message: chat
background set
.. versionadded:: 21.2
paid_media (:obj:`telegram.PaidMediaInfo`): Optional. Message contains paid media;
paid_media (:class:`telegram.PaidMediaInfo`): Optional. Message contains paid media;
information about the paid media.
.. versionadded:: 21.4
refunded_payment (:obj:`telegram.RefundedPayment`): Optional. Message is a service message
about a refunded payment, information about the payment.
refunded_payment (:class:`telegram.RefundedPayment`): Optional. Message is a service
message about a refunded payment, information about the payment.
.. versionadded:: 21.4
@@ -1516,8 +1515,8 @@ class Message(MaybeInaccessibleMessage):
raise RuntimeError("This message has neither text nor caption.")
# Telegram wants the position in UTF-16 code units, so we have to calculate in that space
utf16_text = text.encode("utf-16-le")
utf16_quote = quote.encode("utf-16-le")
utf16_text = text.encode(TextEncoding.UTF_16_LE)
utf16_quote = quote.encode(TextEncoding.UTF_16_LE)
effective_index = index or 0
matches = list(re.finditer(re.escape(utf16_quote), utf16_text))
@@ -4105,11 +4104,18 @@ class Message(MaybeInaccessibleMessage):
"""Shortcut for::
await bot.pin_chat_message(
chat_id=message.chat_id, message_id=message.message_id, *args, **kwargs
chat_id=message.chat_id,
message_id=message.message_id,
business_connection_id=message.business_connection_id,
*args, **kwargs
)
For the documentation of the arguments, please see :meth:`telegram.Bot.pin_chat_message`.
.. versionchanged:: 21.5
Now also passes :attr:`business_connection_id` to
:meth:`telegram.Bot.pin_chat_message`.
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
@@ -4117,6 +4123,7 @@ class Message(MaybeInaccessibleMessage):
return await self.get_bot().pin_chat_message(
chat_id=self.chat_id,
message_id=self.message_id,
business_connection_id=self.business_connection_id,
disable_notification=disable_notification,
read_timeout=read_timeout,
write_timeout=write_timeout,
@@ -4137,11 +4144,18 @@ class Message(MaybeInaccessibleMessage):
"""Shortcut for::
await bot.unpin_chat_message(
chat_id=message.chat_id, message_id=message.message_id, *args, **kwargs
chat_id=message.chat_id,
message_id=message.message_id,
business_connection_id=message.business_connection_id,
*args, **kwargs
)
For the documentation of the arguments, please see :meth:`telegram.Bot.unpin_chat_message`.
.. versionchanged:: 21.5
Now also passes :attr:`business_connection_id` to
:meth:`telegram.Bot.pin_chat_message`.
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
@@ -4149,6 +4163,7 @@ class Message(MaybeInaccessibleMessage):
return await self.get_bot().unpin_chat_message(
chat_id=self.chat_id,
message_id=self.message_id,
business_connection_id=self.business_connection_id,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
@@ -4479,7 +4494,7 @@ class Message(MaybeInaccessibleMessage):
if message_text is None:
return None
utf_16_text = message_text.encode("utf-16-le")
utf_16_text = message_text.encode(TextEncoding.UTF_16_LE)
html_text = ""
last_offset = 0
@@ -4543,7 +4558,9 @@ class Message(MaybeInaccessibleMessage):
# text is part of the parent entity
html_text += (
escape(
utf_16_text[last_offset * 2 : (entity.offset - offset) * 2].decode("utf-16-le")
utf_16_text[last_offset * 2 : (entity.offset - offset) * 2].decode(
TextEncoding.UTF_16_LE
)
)
+ insert
)
@@ -4551,7 +4568,7 @@ class Message(MaybeInaccessibleMessage):
last_offset = entity.offset - offset + entity.length
# see comment above
html_text += escape(utf_16_text[last_offset * 2 :].decode("utf-16-le"))
html_text += escape(utf_16_text[last_offset * 2 :].decode(TextEncoding.UTF_16_LE))
return html_text
@@ -4680,7 +4697,7 @@ class Message(MaybeInaccessibleMessage):
if message_text is None:
return None
utf_16_text = message_text.encode("utf-16-le")
utf_16_text = message_text.encode(TextEncoding.UTF_16_LE)
markdown_text = ""
last_offset = 0
@@ -4773,7 +4790,7 @@ class Message(MaybeInaccessibleMessage):
markdown_text += (
escape_markdown(
utf_16_text[last_offset * 2 : (entity.offset - offset) * 2].decode(
"utf-16-le"
TextEncoding.UTF_16_LE
),
version=version,
)
@@ -4784,7 +4801,7 @@ class Message(MaybeInaccessibleMessage):
# see comment above
markdown_text += escape_markdown(
utf_16_text[last_offset * 2 :].decode("utf-16-le"),
utf_16_text[last_offset * 2 :].decode(TextEncoding.UTF_16_LE),
version=version,
)
+144 -6
View File
@@ -20,17 +20,20 @@
import copy
import itertools
from typing import TYPE_CHECKING, Dict, Final, List, Optional, Sequence
from typing import TYPE_CHECKING, Dict, Final, List, Optional, Sequence, Tuple, Union
from telegram import constants
from telegram._telegramobject import TelegramObject
from telegram._user import User
from telegram._utils import enum
from telegram._utils.strings import TextEncoding
from telegram._utils.types import JSONDict
if TYPE_CHECKING:
from telegram import Bot
_SEM = Sequence["MessageEntity"]
class MessageEntity(TelegramObject):
"""
@@ -145,9 +148,7 @@ class MessageEntity(TelegramObject):
return super().de_json(data=data, bot=bot)
@staticmethod
def adjust_message_entities_to_utf_16(
text: str, entities: Sequence["MessageEntity"]
) -> Sequence["MessageEntity"]:
def adjust_message_entities_to_utf_16(text: str, entities: _SEM) -> _SEM:
"""Utility functionality for converting the offset and length of entities from
Unicode (:obj:`str`) to UTF-16 (``utf-16-le`` encoded :obj:`bytes`).
@@ -203,9 +204,9 @@ class MessageEntity(TelegramObject):
for i, position in enumerate(positions):
last_position = positions[i - 1] if i > 0 else 0
text_slice = text[last_position:position]
accumulated_length += len(text_slice.encode("utf-16-le")) // 2
accumulated_length += len(text_slice.encode(TextEncoding.UTF_16_LE)) // 2
position_translation[position] = accumulated_length
# get the final output entites
# get the final output entities
out = []
for entity in entities:
translated_positions = position_translation[entity.offset]
@@ -219,6 +220,143 @@ class MessageEntity(TelegramObject):
out.append(new_entity)
return out
@staticmethod
def shift_entities(by: Union[str, int], entities: _SEM) -> _SEM:
"""Utility functionality for shifting the offset of entities by a given amount.
Examples:
Shifting by an integer amount:
.. code-block:: python
text = "Hello, world!"
entities = [
MessageEntity(offset=0, length=5, type=MessageEntity.BOLD),
MessageEntity(offset=7, length=5, type=MessageEntity.ITALIC),
]
shifted_entities = MessageEntity.shift_entities(1, entities)
await bot.send_message(
chat_id=123,
text="!" + text,
entities=shifted_entities,
)
Shifting using a string:
.. code-block:: python
text = "Hello, world!"
prefix = "𝄢"
entities = [
MessageEntity(offset=0, length=5, type=MessageEntity.BOLD),
MessageEntity(offset=7, length=5, type=MessageEntity.ITALIC),
]
shifted_entities = MessageEntity.shift_entities(prefix, entities)
await bot.send_message(
chat_id=123,
text=prefix + text,
entities=shifted_entities,
)
Tip:
The :paramref:`entities` are *not* modified in place. The function returns a sequence
of new objects.
.. versionadded:: 21.5
Args:
by (:obj:`str` | :obj:`int`): Either the amount to shift the offset by or
a string whose length will be used as the amount to shift the offset by. In this
case, UTF-16 encoding will be used to calculate the length.
entities (Sequence[:class:`telegram.MessageEntity`]): Sequence of entities
Returns:
Sequence[:class:`telegram.MessageEntity`]: Sequence of entities with the offset shifted
"""
effective_shift = by if isinstance(by, int) else len(by.encode("utf-16-le")) // 2
out = []
for entity in entities:
new_entity = copy.copy(entity)
with new_entity._unfrozen():
new_entity.offset += effective_shift
out.append(new_entity)
return out
@classmethod
def concatenate(
cls,
*args: Union[Tuple[str, _SEM], Tuple[str, _SEM, bool]],
) -> Tuple[str, _SEM]:
"""Utility functionality for concatenating two text along with their formatting entities.
Tip:
This function is useful for prefixing an already formatted text with a new text and its
formatting entities. In particular, it automatically correctly handles UTF-16 encoding.
Examples:
This example shows a callback function that can be used to add a prefix and suffix to
the message in a :class:`~telegram.ext.CallbackQueryHandler`:
.. code-block:: python
async def prefix_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
prefix = "𠌕 bold 𝄢 italic underlined: 𝛙𝌢𑁍 | "
prefix_entities = [
MessageEntity(offset=2, length=4, type=MessageEntity.BOLD),
MessageEntity(offset=9, length=6, type=MessageEntity.ITALIC),
MessageEntity(offset=28, length=3, type=MessageEntity.UNDERLINE),
]
suffix = " | 𠌕 bold 𝄢 italic underlined: 𝛙𝌢𑁍"
suffix_entities = [
MessageEntity(offset=5, length=4, type=MessageEntity.BOLD),
MessageEntity(offset=12, length=6, type=MessageEntity.ITALIC),
MessageEntity(offset=31, length=3, type=MessageEntity.UNDERLINE),
]
message = update.effective_message
first = (prefix, prefix_entities, True)
second = (message.text, message.entities)
third = (suffix, suffix_entities, True)
new_text, new_entities = MessageEntity.concatenate(first, second, third)
await update.callback_query.edit_message_text(
text=new_text,
entities=new_entities,
)
Hint:
The entities are *not* modified in place. The function returns a
new sequence of objects.
.. versionadded:: 21.5
Args:
*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
:meth:`adjust_message_entities_to_utf_16`. UTF-16 adjustment is disabled by
default.
Returns:
Tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`]]: The concatenated text
and its entities
"""
output_text = ""
output_entities: List[MessageEntity] = []
for arg in args:
text, entities = arg[0], arg[1]
if len(arg) > 2 and arg[2] is True:
entities = cls.adjust_message_entities_to_utf_16(text, entities)
output_entities.extend(cls.shift_entities(output_text, entities))
output_text += text
return output_text, output_entities
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
+50
View File
@@ -24,6 +24,7 @@ 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
@@ -288,3 +289,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)
+3 -2
View File
@@ -39,6 +39,7 @@ except ImportError:
from telegram._telegramobject import TelegramObject
from telegram._utils.argumentparsing import parse_sequence_arg
from telegram._utils.strings import TextEncoding
from telegram._utils.types import JSONDict
from telegram.error import PassportDecryptionError
@@ -98,7 +99,7 @@ def decrypt(secret, hash, data):
@no_type_check
def decrypt_json(secret, hash, data):
"""Decrypts data using secret and hash and then decodes utf-8 string and loads json"""
return json.loads(decrypt(secret, hash, data).decode("utf-8"))
return json.loads(decrypt(secret, hash, data).decode(TextEncoding.UTF_8))
class EncryptedCredentials(TelegramObject):
@@ -111,7 +112,7 @@ class EncryptedCredentials(TelegramObject):
Note:
This object is decrypted only when originating from
:obj:`telegram.PassportData.decrypted_credentials`.
:attr:`telegram.PassportData.decrypted_credentials`.
Args:
data (:class:`telegram.Credentials` | :obj:`str`): Decrypted data with unique user's
@@ -42,7 +42,7 @@ class EncryptedPassportElement(TelegramObject):
Note:
This object is decrypted only when originating from
:obj:`telegram.PassportData.decrypted_data`.
:attr:`telegram.PassportData.decrypted_data`.
Args:
type (:obj:`str`): Element type. One of "personal_details", "passport", "driver_license",
+1 -1
View File
@@ -203,7 +203,7 @@ class PassportFile(TelegramObject):
"""
Wrapper over :meth:`telegram.Bot.get_file`. Will automatically assign the correct
credentials to the returned :class:`telegram.File` if originating from
:obj:`telegram.PassportData.decrypted_data`.
:attr:`telegram.PassportData.decrypted_data`.
For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`.
+2
View File
@@ -30,6 +30,8 @@ class RefundedPayment(TelegramObject):
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`telegram_payment_charge_id` is equal.
.. versionadded:: 21.4
Args:
currency (:obj:`str`): Three-letter ISO 4217 `currency
<https://core.telegram.org/bots/payments#supported-currencies>`_ code, or ``XTR`` for
+40 -3
View File
@@ -23,6 +23,7 @@ from datetime import datetime
from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type
from telegram import constants
from telegram._paidmedia import PaidMedia
from telegram._telegramobject import TelegramObject
from telegram._user import User
from telegram._utils import enum
@@ -74,6 +75,17 @@ class RevenueWithdrawalState(TelegramObject):
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["RevenueWithdrawalState"]:
"""Converts JSON data to the appropriate :class:`RevenueWithdrawalState` object, i.e. takes
care of selecting the correct subclass.
Args:
data (Dict[:obj:`str`, ...]): The JSON data.
bot (:class:`telegram.Bot`): The bot associated with this object.
Returns:
The Telegram object.
"""
data = cls._parse_data(data)
if not data:
@@ -150,6 +162,7 @@ class RevenueWithdrawalStateSucceeded(RevenueWithdrawalState):
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["RevenueWithdrawalStateSucceeded"]:
"""See :meth:`telegram.RevenueWithdrawalState.de_json`."""
data = cls._parse_data(data)
if not data:
@@ -260,13 +273,13 @@ class TransactionPartnerFragment(TransactionPartner):
.. versionadded:: 21.4
Args:
withdrawal_state (:obj:`telegram.RevenueWithdrawalState`, optional): State of the
withdrawal_state (:class:`telegram.RevenueWithdrawalState`, optional): State of the
transaction if the transaction is outgoing.
Attributes:
type (:obj:`str`): The type of the transaction partner,
always :tg-const:`telegram.TransactionPartner.FRAGMENT`.
withdrawal_state (:obj:`telegram.RevenueWithdrawalState`): Optional. State of the
withdrawal_state (:class:`telegram.RevenueWithdrawalState`): Optional. State of the
transaction if the transaction is outgoing.
"""
@@ -287,6 +300,7 @@ class TransactionPartnerFragment(TransactionPartner):
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["TransactionPartnerFragment"]:
"""See :meth:`telegram.TransactionPartner.de_json`."""
data = cls._parse_data(data)
if not data:
@@ -310,20 +324,37 @@ class TransactionPartnerUser(TransactionPartner):
Args:
user (:class:`telegram.User`): Information about the user.
invoice_payload (:obj:`str`, optional): Bot-specified invoice payload.
paid_media (Sequence[:class:`telegram.PaidMedia`], optional): Information about the paid
media bought by the user.
.. versionadded:: 21.5
paid_media_payload (:obj:`str`, optional): Optional. Bot-specified paid media payload.
.. versionadded:: 21.6
Attributes:
type (:obj:`str`): The type of the transaction partner,
always :tg-const:`telegram.TransactionPartner.USER`.
user (:class:`telegram.User`): Information about the user.
invoice_payload (:obj:`str`): Optional. Bot-specified invoice payload.
paid_media (Tuple[:class:`telegram.PaidMedia`]): Optional. Information about the paid
media bought by the user.
.. versionadded:: 21.5
paid_media_payload (:obj:`str`): Optional. Optional. Bot-specified paid media payload.
.. versionadded:: 21.6
"""
__slots__ = ("invoice_payload", "user")
__slots__ = ("invoice_payload", "paid_media", "paid_media_payload", "user")
def __init__(
self,
user: "User",
invoice_payload: Optional[str] = None,
paid_media: Optional[Sequence[PaidMedia]] = None,
paid_media_payload: Optional[str] = None,
*,
api_kwargs: Optional[JSONDict] = None,
) -> None:
@@ -332,6 +363,8 @@ class TransactionPartnerUser(TransactionPartner):
with self._unfrozen():
self.user: User = user
self.invoice_payload: Optional[str] = invoice_payload
self.paid_media: Optional[Tuple[PaidMedia, ...]] = parse_sequence_arg(paid_media)
self.paid_media_payload: Optional[str] = paid_media_payload
self._id_attrs = (
self.type,
self.user,
@@ -341,12 +374,14 @@ class TransactionPartnerUser(TransactionPartner):
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["TransactionPartnerUser"]:
"""See :meth:`telegram.TransactionPartner.de_json`."""
data = cls._parse_data(data)
if not data:
return None
data["user"] = User.de_json(data.get("user"), bot)
data["paid_media"] = PaidMedia.de_list(data.get("paid_media"), bot=bot)
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
@@ -452,6 +487,7 @@ class StarTransaction(TelegramObject):
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["StarTransaction"]:
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
if not data:
@@ -498,6 +534,7 @@ class StarTransactions(TelegramObject):
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["StarTransactions"]:
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
if data is None:
+47 -11
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 represents a Telegram ReactionType."""
from typing import TYPE_CHECKING, Final, Literal, Optional, Union
from typing import TYPE_CHECKING, Dict, Final, Literal, Optional, Type, Union
from telegram import constants
from telegram._telegramobject import TelegramObject
@@ -30,16 +31,22 @@ if TYPE_CHECKING:
class ReactionType(TelegramObject):
"""Base class for Telegram ReactionType Objects.
There exist :class:`telegram.ReactionTypeEmoji` and :class:`telegram.ReactionTypeCustomEmoji`.
There exist :class:`telegram.ReactionTypeEmoji`, :class:`telegram.ReactionTypeCustomEmoji`
and :class:`telegram.ReactionTypePaid`.
.. versionadded:: 20.8
.. versionchanged:: 21.5
Added paid reaction.
Args:
type (:obj:`str`): Type of the reaction. Can be
:attr:`~telegram.ReactionType.EMOJI` or :attr:`~telegram.ReactionType.CUSTOM_EMOJI`.
:attr:`~telegram.ReactionType.EMOJI`, :attr:`~telegram.ReactionType.CUSTOM_EMOJI` or
:attr:`~telegram.ReactionType.PAID`.
Attributes:
type (:obj:`str`): Type of the reaction. Can be
:attr:`~telegram.ReactionType.EMOJI` or :attr:`~telegram.ReactionType.CUSTOM_EMOJI`.
:attr:`~telegram.ReactionType.EMOJI`, :attr:`~telegram.ReactionType.CUSTOM_EMOJI` or
:attr:`~telegram.ReactionType.PAID`.
"""
@@ -49,11 +56,16 @@ class ReactionType(TelegramObject):
""":const:`telegram.constants.ReactionType.EMOJI`"""
CUSTOM_EMOJI: Final[constants.ReactionType] = constants.ReactionType.CUSTOM_EMOJI
""":const:`telegram.constants.ReactionType.CUSTOM_EMOJI`"""
PAID: Final[constants.ReactionType] = constants.ReactionType.PAID
""":const:`telegram.constants.ReactionType.PAID`
.. versionadded:: 21.5
"""
def __init__(
self,
type: Union[ # pylint: disable=redefined-builtin
Literal["emoji", "custom_emoji"], constants.ReactionType
Literal["emoji", "custom_emoji", "paid"], constants.ReactionType
],
*,
api_kwargs: Optional[JSONDict] = None,
@@ -71,14 +83,20 @@ class ReactionType(TelegramObject):
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
if not data:
if data is None:
return None
if cls is ReactionType and data.get("type") in [cls.EMOJI, cls.CUSTOM_EMOJI]:
reaction_type = data.pop("type")
if reaction_type == cls.EMOJI:
return ReactionTypeEmoji.de_json(data=data, bot=bot)
return ReactionTypeCustomEmoji.de_json(data=data, bot=bot)
if not data and cls is ReactionType:
return None
_class_mapping: Dict[str, Type[ReactionType]] = {
cls.EMOJI: ReactionTypeEmoji,
cls.CUSTOM_EMOJI: ReactionTypeCustomEmoji,
cls.PAID: ReactionTypePaid,
}
if cls is ReactionType and data.get("type") in _class_mapping:
return _class_mapping[data.pop("type")].de_json(data, bot)
return super().de_json(data=data, bot=bot)
@@ -152,6 +170,24 @@ class ReactionTypeCustomEmoji(ReactionType):
self._id_attrs = (self.custom_emoji_id,)
class ReactionTypePaid(ReactionType):
"""
The reaction is paid.
.. versionadded:: 21.5
Attributes:
type (:obj:`str`): Type of the reaction,
always :tg-const:`telegram.ReactionType.PAID`.
"""
__slots__ = ()
def __init__(self, *, api_kwargs: Optional[JSONDict] = None):
super().__init__(type=ReactionType.PAID, api_kwargs=api_kwargs)
self._freeze()
class ReactionCount(TelegramObject):
"""This class represents a reaction added to a message along with the number of times it was
added.
+14 -14
View File
@@ -92,14 +92,13 @@ class ExternalReplyInfo(TelegramObject):
about the contact.
dice (:class:`telegram.Dice`, optional): Message is a dice with random value.
game (:Class:`telegram.Game`. optional): Message is a game, information about the game.
`More about games >> <https://core.telegram.org/bots/api#games>`_.
:ref:`More about games >> <games-tree>`.
giveaway (:class:`telegram.Giveaway`, optional): Message is a scheduled giveaway,
information about the giveaway.
giveaway_winners (:class:`telegram.GiveawayWinners`, optional): A giveaway with public
winners was completed.
invoice (:class:`telegram.Invoice`, optional): Message is an invoice for a payment,
information about the invoice. `More about payments >>
<https://core.telegram.org/bots/api#payments>`_.
information about the invoice. :ref:`More about payments >> <payments-tree>`.
location (:class:`telegram.Location`, optional): Message is a shared location, information
about the location.
poll (:class:`telegram.Poll`, optional): Message is a native poll, information about the
@@ -142,14 +141,13 @@ class ExternalReplyInfo(TelegramObject):
about the contact.
dice (:class:`telegram.Dice`): Optional. Message is a dice with random value.
game (:Class:`telegram.Game`): Optional. Message is a game, information about the game.
`More about games >> <https://core.telegram.org/bots/api#games>`_.
:ref:`More about games >> <games-tree>`.
giveaway (:class:`telegram.Giveaway`): Optional. Message is a scheduled giveaway,
information about the giveaway.
giveaway_winners (:class:`telegram.GiveawayWinners`): Optional. A giveaway with public
winners was completed.
invoice (:class:`telegram.Invoice`): Optional. Message is an invoice for a payment,
information about the invoice. `More about payments >>
<https://core.telegram.org/bots/api#payments>`_.
information about the invoice. :ref:`More about payments >> <payments-tree>`.
location (:class:`telegram.Location`): Optional. Message is a shared location, information
about the location.
poll (:class:`telegram.Poll`): Optional. Message is a native poll, information about the
@@ -252,7 +250,7 @@ class ExternalReplyInfo(TelegramObject):
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["ExternalReplyInfo"]:
"""See :obj:`telegram.TelegramObject.de_json`."""
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
if data is None:
@@ -301,7 +299,8 @@ class TextQuote(TelegramObject):
message.
position (:obj:`int`): Approximate quote position in the original message in UTF-16 code
units as specified by the sender.
entities (Sequence[:obj:`telegram.MessageEntity`], optional): Special entities that appear
entities (Sequence[:class:`telegram.MessageEntity`], optional): Special entities that
appear
in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and
custom_emoji entities are kept in quotes.
is_manual (:obj:`bool`, optional): :obj:`True`, if the quote was chosen manually by the
@@ -312,7 +311,7 @@ class TextQuote(TelegramObject):
message.
position (:obj:`int`): Approximate quote position in the original message in UTF-16 code
units as specified by the sender.
entities (Tuple[:obj:`telegram.MessageEntity`]): Optional. Special entities that appear
entities (Tuple[:class:`telegram.MessageEntity`]): Optional. Special entities that appear
in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and
custom_emoji entities are kept in quotes.
is_manual (:obj:`bool`): Optional. :obj:`True`, if the quote was chosen manually by the
@@ -353,7 +352,7 @@ class TextQuote(TelegramObject):
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["TextQuote"]:
"""See :obj:`telegram.TelegramObject.de_json`."""
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
if data is None:
@@ -389,7 +388,8 @@ class ReplyParameters(TelegramObject):
quote_parse_mode (:obj:`str`, optional): Mode for parsing entities in the quote. See
:wiki:`formatting options <Code-snippets#message-formatting-bold-italic-code->` for
more details.
quote_entities (Sequence[:obj:`telegram.MessageEntity`], optional): A JSON-serialized list
quote_entities (Sequence[:class:`telegram.MessageEntity`], optional): A JSON-serialized
list
of special entities that appear in the quote. It can be specified instead of
:paramref:`quote_parse_mode`.
quote_position (:obj:`int`, optional): Position of the quote in the original message in
@@ -411,8 +411,8 @@ class ReplyParameters(TelegramObject):
quote_parse_mode (:obj:`str`): Optional. Mode for parsing entities in the quote. See
:wiki:`formatting options <Code-snippets#message-formatting-bold-italic-code->` for
more details.
quote_entities (Tuple[:obj:`telegram.MessageEntity`]): Optional. A JSON-serialized list of
special entities that appear in the quote. It can be specified instead of
quote_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. A JSON-serialized list
of special entities that appear in the quote. It can be specified instead of
:paramref:`quote_parse_mode`.
quote_position (:obj:`int`): Optional. Position of the quote in the original message in
UTF-16 code units.
@@ -460,7 +460,7 @@ class ReplyParameters(TelegramObject):
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["ReplyParameters"]:
"""See :obj:`telegram.TelegramObject.de_json`."""
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data)
if data is None:
+2 -2
View File
@@ -354,7 +354,7 @@ class TelegramObject:
memodict (:obj:`dict`): A dictionary that maps objects to their copies.
Returns:
:obj:`telegram.TelegramObject`: The copied object.
:class:`telegram.TelegramObject`: The copied object.
"""
bot = self._bot # Save bot so we can set it after copying
self.set_bot(None) # set to None so it is not deepcopied
@@ -414,7 +414,7 @@ class TelegramObject:
obj = cls(**data, api_kwargs=api_kwargs)
except TypeError as exc:
if "__init__() got an unexpected keyword argument" not in str(exc):
raise exc
raise
if cls.__INIT_PARAMS_CHECK is not cls:
signature = inspect.signature(cls)
+32 -1
View File
@@ -30,6 +30,7 @@ from telegram._choseninlineresult import ChosenInlineResult
from telegram._inline.inlinequery import InlineQuery
from telegram._message import Message
from telegram._messagereactionupdated import MessageReactionCountUpdated, MessageReactionUpdated
from telegram._paidmedia import PaidMediaPurchased
from telegram._payment.precheckoutquery import PreCheckoutQuery
from telegram._payment.shippingquery import ShippingQuery
from telegram._poll import Poll, PollAnswer
@@ -156,6 +157,11 @@ class Update(TelegramObject):
.. versionadded:: 21.1
purchased_paid_media (:class:`telegram.PaidMediaPurchased`, optional): A user purchased
paid media with a non-empty payload sent by the bot in a non-channel chat.
.. versionadded:: 21.6
Attributes:
update_id (:obj:`int`): The update's unique identifier. Update identifiers start from a
@@ -263,6 +269,11 @@ class Update(TelegramObject):
were deleted from a connected business account.
.. versionadded:: 21.1
purchased_paid_media (:class:`telegram.PaidMediaPurchased`): Optional. A user purchased
paid media with a non-empty payload sent by the bot in a non-channel chat.
.. versionadded:: 21.6
"""
__slots__ = (
@@ -290,6 +301,7 @@ class Update(TelegramObject):
"poll",
"poll_answer",
"pre_checkout_query",
"purchased_paid_media",
"removed_chat_boost",
"shipping_query",
"update_id",
@@ -383,6 +395,13 @@ class Update(TelegramObject):
""":const:`telegram.constants.UpdateType.DELETED_BUSINESS_MESSAGES`
.. versionadded:: 21.1"""
PURCHASED_PAID_MEDIA: Final[str] = constants.UpdateType.PURCHASED_PAID_MEDIA
""":const:`telegram.constants.UpdateType.PURCHASED_PAID_MEDIA`
.. versionadded:: 21.6
"""
ALL_TYPES: Final[List[str]] = list(constants.UpdateType)
"""List[:obj:`str`]: A list of all available update types.
@@ -413,6 +432,7 @@ class Update(TelegramObject):
business_message: Optional[Message] = None,
edited_business_message: Optional[Message] = None,
deleted_business_messages: Optional[BusinessMessagesDeleted] = None,
purchased_paid_media: Optional[PaidMediaPurchased] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
@@ -444,6 +464,7 @@ class Update(TelegramObject):
self.deleted_business_messages: Optional[BusinessMessagesDeleted] = (
deleted_business_messages
)
self.purchased_paid_media: Optional[PaidMediaPurchased] = purchased_paid_media
self._effective_user: Optional[User] = None
self._effective_sender: Optional[Union[User, Chat]] = None
@@ -475,6 +496,9 @@ class Update(TelegramObject):
This property now also considers :attr:`business_connection`, :attr:`business_message`
and :attr:`edited_business_message`.
.. versionchanged:: 21.6
This property now also considers :attr:`purchased_paid_media`.
Example:
* If :attr:`message` is present, this will give
:attr:`telegram.Message.from_user`.
@@ -531,6 +555,9 @@ class Update(TelegramObject):
elif self.business_connection:
user = self.business_connection.user
elif self.purchased_paid_media:
user = self.purchased_paid_media.from_user
self._effective_user = user
return user
@@ -601,7 +628,8 @@ class Update(TelegramObject):
This is the case, if :attr:`inline_query`,
:attr:`chosen_inline_result`, :attr:`callback_query` from inline messages,
:attr:`shipping_query`, :attr:`pre_checkout_query`, :attr:`poll`,
:attr:`poll_answer`, or :attr:`business_connection` is present.
:attr:`poll_answer`, :attr:`business_connection`, or :attr:`purchased_paid_media`
is present.
.. versionchanged:: 21.1
This property now also considers :attr:`business_message`,
@@ -768,5 +796,8 @@ class Update(TelegramObject):
data["deleted_business_messages"] = BusinessMessagesDeleted.de_json(
data.get("deleted_business_messages"), bot
)
data["purchased_paid_media"] = PaidMediaPurchased.de_json(
data.get("purchased_paid_media"), bot
)
return super().de_json(data=data, bot=bot)
+16
View File
@@ -97,6 +97,10 @@ class User(TelegramObject):
:meth:`telegram.Bot.get_me`.
.. versionadded:: 21.1
has_main_web_app (:obj:`bool`, optional): :obj:`True`, if the bot has the main Web App.
Returned only in :meth:`telegram.Bot.get_me`.
.. versionadded:: 21.5
Attributes:
id (:obj:`int`): Unique identifier for this user or bot.
@@ -124,6 +128,11 @@ class User(TelegramObject):
:meth:`telegram.Bot.get_me`.
.. versionadded:: 21.1
has_main_web_app (:obj:`bool`) Optional. :obj:`True`, if the bot has the main Web App.
Returned only in :meth:`telegram.Bot.get_me`.
.. versionadded:: 21.5
.. |user_chat_id_note| replace:: This shortcuts build on the assumption that :attr:`User.id`
coincides with the :attr:`Chat.id` of the private chat with the user. This has been the
case so far, but Telegram does not guarantee that this stays this way.
@@ -135,6 +144,7 @@ class User(TelegramObject):
"can_join_groups",
"can_read_all_group_messages",
"first_name",
"has_main_web_app",
"id",
"is_bot",
"is_premium",
@@ -158,6 +168,7 @@ class User(TelegramObject):
is_premium: Optional[bool] = None,
added_to_attachment_menu: Optional[bool] = None,
can_connect_to_business: Optional[bool] = None,
has_main_web_app: Optional[bool] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
@@ -176,6 +187,7 @@ class User(TelegramObject):
self.is_premium: Optional[bool] = is_premium
self.added_to_attachment_menu: Optional[bool] = added_to_attachment_menu
self.can_connect_to_business: Optional[bool] = can_connect_to_business
self.has_main_web_app: Optional[bool] = has_main_web_app
self._id_attrs = (self.id,)
@@ -301,6 +313,7 @@ class User(TelegramObject):
self,
message_id: int,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
business_connection_id: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -329,12 +342,14 @@ class User(TelegramObject):
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
business_connection_id=business_connection_id,
api_kwargs=api_kwargs,
)
async def unpin_message(
self,
message_id: Optional[int] = None,
business_connection_id: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -363,6 +378,7 @@ class User(TelegramObject):
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
message_id=message_id,
business_connection_id=business_connection_id,
)
async def unpin_all_messages(
+3 -2
View File
@@ -26,6 +26,7 @@ Warning:
from typing import Dict, Optional, Sequence
from telegram._messageentity import MessageEntity
from telegram._utils.strings import TextEncoding
def parse_message_entity(text: str, entity: MessageEntity) -> str:
@@ -38,10 +39,10 @@ def parse_message_entity(text: str, entity: MessageEntity) -> str:
Returns:
:obj:`str`: The text of the given entity.
"""
entity_text = text.encode("utf-16-le")
entity_text = text.encode(TextEncoding.UTF_16_LE)
entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2]
return entity_text.decode("utf-16-le")
return entity_text.decode(TextEncoding.UTF_16_LE)
def parse_message_entities(
+13 -6
View File
@@ -61,14 +61,21 @@ def load_file(
except AttributeError:
return None, cast(Union[bytes, "InputFile", str, Path], obj)
if hasattr(obj, "name") and not isinstance(obj.name, int):
filename = Path(obj.name).name
else:
filename = None
filename = guess_file_name(obj)
return filename, contents
def guess_file_name(obj: FileInput) -> Optional[str]:
"""If the input is a file handle, read name and return it. Otherwise, return
the input unchanged.
"""
if hasattr(obj, "name") and not isinstance(obj.name, int):
return Path(obj.name).name
return None
def is_local_file(obj: Optional[FilePathInput]) -> bool:
"""
Checks if a given string is a file on local system.
@@ -110,8 +117,8 @@ def parse_file_input( # pylint: disable=too-many-return-statements
attribute.
Args:
file_input (:obj:`str` | :obj:`bytes` | :term:`file object` | Telegram media object): The
input to parse.
file_input (:obj:`str` | :obj:`bytes` | :term:`file object` | :class:`~telegram.InputFile`\
| Telegram media object): The input to parse.
tg_type (:obj:`type`, optional): The Telegram media type the input can be. E.g.
:class:`telegram.Animation`.
filename (:obj:`str`, optional): The filename. Only relevant in case an
+17
View File
@@ -24,6 +24,23 @@ Warning:
the changelog.
"""
from telegram._utils.enum import StringEnum
# TODO: Remove this when https://github.com/PyCQA/pylint/issues/6887 is resolved.
# pylint: disable=invalid-enum-extension,invalid-slots
class TextEncoding(StringEnum):
"""This enum contains encoding schemes for text.
.. versionadded:: 21.5
"""
__slots__ = ()
UTF_8 = "utf-8"
UTF_16_LE = "utf-16-le"
def to_camel_case(snake_str: str) -> str:
"""Converts a snake_case string to camelCase.
+1 -1
View File
@@ -82,7 +82,7 @@ ReplyMarkup = Union[
.. versionadded:: 20.0
"""
FieldTuple = Tuple[str, bytes, str]
FieldTuple = Tuple[str, Union[bytes, IO[bytes]], str]
"""Alias for return type of `InputFile.field_tuple`."""
UploadFileDict = Dict[str, FieldTuple]
"""Dictionary containing file data to be uploaded to the API."""
+1 -1
View File
@@ -51,6 +51,6 @@ class Version(NamedTuple):
__version_info__: Final[Version] = Version(
major=21, minor=4, micro=0, releaselevel="final", serial=0
major=21, minor=6, micro=0, releaselevel="final", serial=0
)
__version__: Final[str] = str(__version_info__)
+83 -37
View File
@@ -54,6 +54,7 @@ __all__ = [
"ChatLimit",
"ChatMemberStatus",
"ChatPhotoSize",
"ChatSubscriptionLimit",
"ChatType",
"ContactLimit",
"CustomEmojiStickerLimit",
@@ -151,7 +152,7 @@ class _AccentColor(NamedTuple):
#: :data:`telegram.__bot_api_version_info__`.
#:
#: .. versionadded:: 20.0
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=7)
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=10)
#: :obj:`str`: Telegram Bot API
#: version supported by this version of `python-telegram-bot`. Also available as
#: :data:`telegram.__bot_api_version__`.
@@ -551,6 +552,42 @@ class AccentColor(Enum):
"""
class BackgroundTypeType(StringEnum):
"""This enum contains the available types of :class:`telegram.BackgroundType`. The enum
members of this enumeration are instances of :class:`str` and can be treated as such.
.. versionadded:: 21.2
"""
__slots__ = ()
FILL = "fill"
""":obj:`str`: A :class:`telegram.BackgroundType` with fill background."""
WALLPAPER = "wallpaper"
""":obj:`str`: A :class:`telegram.BackgroundType` with wallpaper background."""
PATTERN = "pattern"
""":obj:`str`: A :class:`telegram.BackgroundType` with pattern background."""
CHAT_THEME = "chat_theme"
""":obj:`str`: A :class:`telegram.BackgroundType` with chat_theme background."""
class BackgroundFillType(StringEnum):
"""This enum contains the available types of :class:`telegram.BackgroundFill`. The enum
members of this enumeration are instances of :class:`str` and can be treated as such.
.. versionadded:: 21.2
"""
__slots__ = ()
SOLID = "solid"
""":obj:`str`: A :class:`telegram.BackgroundFill` with solid fill."""
GRADIENT = "gradient"
""":obj:`str`: A :class:`telegram.BackgroundFill` with gradient fill."""
FREEFORM_GRADIENT = "freeform_gradient"
""":obj:`str`: A :class:`telegram.BackgroundFill` with freeform_gradient fill."""
class BotCommandLimit(IntEnum):
"""This enum contains limitations for :class:`telegram.BotCommand` and
:meth:`telegram.Bot.set_my_commands`.
@@ -832,6 +869,25 @@ class ChatLimit(IntEnum):
"""
class ChatSubscriptionLimit(IntEnum):
"""This enum contains limitations for
:paramref:`telegram.Bot.create_chat_subscription_invite_link.subscription_period` and
:paramref:`telegram.Bot.create_chat_subscription_invite_link.subscription_price`.
The enum members of this enumeration are instances of :class:`int` and can be treated as such.
.. versionadded:: 21.5
"""
__slots__ = ()
SUBSCRIPTION_PERIOD = 2592000
""":obj:`int`: The number of seconds the subscription will be active."""
MIN_PRICE = 1
""":obj:`int`: Amount of stars a user pays, minimum amount the subscription can be set to."""
MAX_PRICE = 2500
""":obj:`int`: Amount of stars a user pays, maximum amount the subscription can be set to."""
class BackgroundTypeLimit(IntEnum):
"""This enum contains limitations for :class:`telegram.BackgroundTypeFill`,
:class:`telegram.BackgroundTypeWallpaper` and :class:`telegram.BackgroundTypePattern`.
@@ -2723,6 +2779,11 @@ class UpdateType(StringEnum):
.. versionadded:: 21.1
"""
PURCHASED_PAID_MEDIA = "purchased_paid_media"
""":obj:`str`: Updates with :attr:`telegram.Update.purchased_paid_media`.
.. versionadded:: 21.6
"""
class InvoiceLimit(IntEnum):
@@ -2794,6 +2855,8 @@ class InvoiceLimit(IntEnum):
:meth:`telegram.Bot.send_invoice`.
* :paramref:`~telegram.Bot.create_invoice_link.payload` parameter of
:meth:`telegram.Bot.create_invoice_link`.
* :paramref:`~telegram.Bot.send_paid_media.payload` parameter of
:meth:`telegram.Bot.send_paid_media`.
"""
MAX_TIP_AMOUNTS = 4
""":obj:`int`: Maximum length of a :obj:`Sequence` passed as:
@@ -2803,6 +2866,20 @@ class InvoiceLimit(IntEnum):
* :paramref:`~telegram.Bot.create_invoice_link.suggested_tip_amounts` parameter of
:meth:`telegram.Bot.create_invoice_link`.
"""
MIN_STAR_COUNT = 1
""":obj:`int`: Minimum amount of starts that must be paid to buy access to a paid media
passed as :paramref:`~telegram.Bot.send_paid_media.star_count` parameter of
:meth:`telegram.Bot.send_paid_media`.
.. versionadded:: 21.6
"""
MAX_STAR_COUNT = 2500
""":obj:`int`: Maximum amount of starts that must be paid to buy access to a paid media
passed as :paramref:`~telegram.Bot.send_paid_media.star_count` parameter of
:meth:`telegram.Bot.send_paid_media`.
.. versionadded:: 21.6
"""
class UserProfilePhotosLimit(IntEnum):
@@ -2903,6 +2980,11 @@ class ReactionType(StringEnum):
""":obj:`str`: A :class:`telegram.ReactionType` with a normal emoji."""
CUSTOM_EMOJI = "custom_emoji"
""":obj:`str`: A :class:`telegram.ReactionType` with a custom emoji."""
PAID = "paid"
""":obj:`str`: A :class:`telegram.ReactionType` with a paid reaction.
.. versionadded:: 21.5
"""
class ReactionEmoji(StringEnum):
@@ -3060,39 +3142,3 @@ class ReactionEmoji(StringEnum):
""":obj:`str`: Woman Shrugging"""
POUTING_FACE = "😡"
""":obj:`str`: Pouting face"""
class BackgroundTypeType(StringEnum):
"""This enum contains the available types of :class:`telegram.BackgroundType`. The enum
members of this enumeration are instances of :class:`str` and can be treated as such.
.. versionadded:: 21.2
"""
__slots__ = ()
FILL = "fill"
""":obj:`str`: A :class:`telegram.BackgroundType` with fill background."""
WALLPAPER = "wallpaper"
""":obj:`str`: A :class:`telegram.BackgroundType` with wallpaper background."""
PATTERN = "pattern"
""":obj:`str`: A :class:`telegram.BackgroundType` with pattern background."""
CHAT_THEME = "chat_theme"
""":obj:`str`: A :class:`telegram.BackgroundType` with chat_theme background."""
class BackgroundFillType(StringEnum):
"""This enum contains the available types of :class:`telegram.BackgroundFill`. The enum
members of this enumeration are instances of :class:`str` and can be treated as such.
.. versionadded:: 21.2
"""
__slots__ = ()
SOLID = "solid"
""":obj:`str`: A :class:`telegram.BackgroundFill` with solid fill."""
GRADIENT = "gradient"
""":obj:`str`: A :class:`telegram.BackgroundFill` with gradient fill."""
FREEFORM_GRADIENT = "freeform_gradient"
""":obj:`str`: A :class:`telegram.BackgroundFill` with freeform_gradient fill."""
+2
View File
@@ -48,6 +48,7 @@ __all__ = (
"JobQueue",
"MessageHandler",
"MessageReactionHandler",
"PaidMediaPurchasedHandler",
"PersistenceInput",
"PicklePersistence",
"PollAnswerHandler",
@@ -89,6 +90,7 @@ from ._handlers.conversationhandler import ConversationHandler
from ._handlers.inlinequeryhandler import InlineQueryHandler
from ._handlers.messagehandler import MessageHandler
from ._handlers.messagereactionhandler import MessageReactionHandler
from ._handlers.paidmediapurchasedhandler import PaidMediaPurchasedHandler
from ._handlers.pollanswerhandler import PollAnswerHandler
from ._handlers.pollhandler import PollHandler
from ._handlers.precheckoutqueryhandler import PreCheckoutQueryHandler
+1 -1
View File
@@ -250,7 +250,7 @@ class AIORateLimiter(BaseRateLimiter[int]):
_LOGGER.exception(
"Rate limit hit after maximum of %d retries", max_retries, exc_info=exc
)
raise exc
raise
sleep = exc.retry_after + 0.1
_LOGGER.info("Rate limit hit. Retrying after %f seconds", sleep)
+20 -15
View File
@@ -323,7 +323,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
self.update_queue: asyncio.Queue[object] = update_queue
self.context_types: ContextTypes[CCT, UD, CD, BD] = context_types
self.updater: Optional[Updater] = updater
self.handlers: Dict[int, List[BaseHandler[Any, CCT]]] = {}
self.handlers: Dict[int, List[BaseHandler[Any, CCT, Any]]] = {}
self.error_handlers: Dict[
HandlerCallback[object, CCT, None], Union[bool, DefaultValue[bool]]
] = {}
@@ -384,10 +384,10 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
"""
try:
await self.initialize()
return self
except Exception as exc:
except Exception:
await self.shutdown()
raise exc
raise
return self
async def __aexit__(
self,
@@ -646,9 +646,9 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
)
_LOGGER.info("Application started")
except Exception as exc:
except Exception:
self._running = False
raise exc
raise
async def stop(self) -> None:
"""Stops the process after processing any pending updates or tasks created by
@@ -1227,7 +1227,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
await self.process_error(update=update, error=exception, coroutine=coroutine)
# Raise exception so that it can be set on the task and retrieved by task.exception()
raise exception
raise
finally:
self._mark_for_persistence_update(update=update)
@@ -1352,7 +1352,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
# (in __create_task_callback)
self._mark_for_persistence_update(update=update)
def add_handler(self, handler: BaseHandler[Any, CCT], group: int = DEFAULT_GROUP) -> None:
def add_handler(self, handler: BaseHandler[Any, CCT, Any], group: int = DEFAULT_GROUP) -> None:
"""Register a handler.
TL;DR: Order and priority counts. 0 or 1 handlers per group will be used. End handling of
@@ -1420,8 +1420,8 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
def add_handlers(
self,
handlers: Union[
Union[List[BaseHandler[Any, CCT]], Tuple[BaseHandler[Any, CCT]]],
Dict[int, Union[List[BaseHandler[Any, CCT]], Tuple[BaseHandler[Any, CCT]]]],
Union[List[BaseHandler[Any, CCT, Any]], Tuple[BaseHandler[Any, CCT, Any]]],
Dict[int, Union[List[BaseHandler[Any, CCT, Any]], Tuple[BaseHandler[Any, CCT, Any]]]],
],
group: Union[int, DefaultValue[int]] = _DEFAULT_0,
) -> None:
@@ -1445,14 +1445,16 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
1: [CallbackQueryHandler(...), CommandHandler(...)]
}
Raises:
:exc:`TypeError`: If the combination of arguments is invalid.
"""
if isinstance(handlers, dict) and not isinstance(group, DefaultValue):
raise ValueError("The `group` argument can only be used with a sequence of handlers.")
raise TypeError("The `group` argument can only be used with a sequence of handlers.")
if isinstance(handlers, dict):
for handler_group, grp_handlers in handlers.items():
if not isinstance(grp_handlers, (list, tuple)):
raise ValueError(f"Handlers for group {handler_group} must be a list or tuple")
raise TypeError(f"Handlers for group {handler_group} must be a list or tuple")
for handler in grp_handlers:
self.add_handler(handler, handler_group)
@@ -1462,12 +1464,14 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
self.add_handler(handler, DefaultValue.get_value(group))
else:
raise ValueError(
raise TypeError(
"The `handlers` argument must be a sequence of handlers or a "
"dictionary where the keys are groups and values are sequences of handlers."
)
def remove_handler(self, handler: BaseHandler[Any, CCT], group: int = DEFAULT_GROUP) -> None:
def remove_handler(
self, handler: BaseHandler[Any, CCT, Any], group: int = DEFAULT_GROUP
) -> None:
"""Remove a handler from the specified group.
Args:
@@ -1644,9 +1648,10 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
self.__update_persistence_event.wait(),
timeout=self.persistence.update_interval,
)
return
except asyncio.TimeoutError:
pass
else:
return
# putting this *after* the wait_for so we don't immediately update on startup as
# that would make little sense
+1 -1
View File
@@ -207,7 +207,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
self._job_queue: ODVInput[JobQueue] = DefaultValue(JobQueue())
except RuntimeError as exc:
if "PTB must be installed via" not in str(exc):
raise exc
raise
self._job_queue = DEFAULT_NONE
self._persistence: ODVInput[BasePersistence] = DEFAULT_NONE
+3 -3
View File
@@ -81,10 +81,10 @@ class BaseUpdateProcessor(AsyncContextManager["BaseUpdateProcessor"], ABC):
"""
try:
await self.initialize()
return self
except Exception as exc:
except Exception:
await self.shutdown()
raise exc
raise
return self
async def __aexit__(
self,
+13 -7
View File
@@ -29,6 +29,7 @@ from typing import (
NoReturn,
Optional,
Type,
TypeVar,
Union,
)
@@ -49,6 +50,9 @@ _STORING_DATA_WIKI = (
"/wiki/Storing-bot%2C-user-and-chat-related-data"
)
# something like poor mans "typing.Self" for py<3.11
ST = TypeVar("ST", bound="CallbackContext[Any, Any, Any, Any]")
class CallbackContext(Generic[BT, UD, CD, BD]):
"""
@@ -133,24 +137,24 @@ class CallbackContext(Generic[BT, UD, CD, BD]):
)
def __init__(
self: "CCT",
application: "Application[BT, CCT, UD, CD, BD, Any]",
self: ST,
application: "Application[BT, ST, UD, CD, BD, Any]",
chat_id: Optional[int] = None,
user_id: Optional[int] = None,
):
self._application: Application[BT, CCT, UD, CD, BD, Any] = application
self._application: Application[BT, ST, UD, CD, BD, Any] = application
self._chat_id: Optional[int] = chat_id
self._user_id: Optional[int] = user_id
self.args: Optional[List[str]] = None
self.matches: Optional[List[Match[str]]] = None
self.error: Optional[Exception] = None
self.job: Optional[Job[CCT]] = None
self.job: Optional[Job[Any]] = None
self.coroutine: Optional[
Union[Generator[Optional[Future[object]], None, Any], Awaitable[Any]]
] = None
@property
def application(self) -> "Application[BT, CCT, UD, CD, BD, Any]":
def application(self) -> "Application[BT, ST, UD, CD, BD, Any]":
""":class:`telegram.ext.Application`: The application associated with this context."""
return self._application
@@ -272,7 +276,9 @@ class CallbackContext(Generic[BT, UD, CD, BD]):
)
self.bot.callback_data_cache.drop_data(callback_query)
else:
raise RuntimeError("telegram.Bot does not allow for arbitrary callback data.")
raise RuntimeError( # noqa: TRY004
"telegram.Bot does not allow for arbitrary callback data."
)
@classmethod
def from_error(
@@ -396,7 +402,7 @@ class CallbackContext(Generic[BT, UD, CD, BD]):
return self._application.bot
@property
def job_queue(self) -> Optional["JobQueue[CCT]"]:
def job_queue(self) -> Optional["JobQueue[ST]"]:
"""
:class:`telegram.ext.JobQueue`: The :class:`JobQueue` used by the
:class:`telegram.ext.Application`.
+1 -1
View File
@@ -278,9 +278,9 @@ class CallbackDataCache:
button_data = keyboard_data.button_data[button]
# Update the timestamp for the LRU
keyboard_data.update_access_time()
return keyboard, button_data
except KeyError:
return None, InvalidCallbackData(callback_data)
return keyboard, button_data
@staticmethod
def extract_uuids(callback_data: str) -> Tuple[str, str]:
+1 -1
View File
@@ -188,7 +188,7 @@ class ContextTypes(Generic[CCT, UD, CD, BD]):
user_data: Type[ADict] = dict,
):
if not issubclass(context, CallbackContext):
raise ValueError("context must be a subclass of CallbackContext.")
raise TypeError("context must be a subclass of CallbackContext.")
# We make all those only accessible via properties because we don't currently support
# changing this at runtime, so overriding the attributes doesn't make sense
+61 -1
View File
@@ -637,7 +637,7 @@ class ExtBot(Bot, Generic[RLARGS]):
self,
offset: Optional[int] = None,
limit: Optional[int] = None,
timeout: Optional[int] = None,
timeout: Optional[int] = None, # noqa: ASYNC109
allowed_updates: Optional[Sequence[str]] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -2238,6 +2238,7 @@ class ExtBot(Bot, Generic[RLARGS]):
chat_id: Union[str, int],
message_id: int,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
business_connection_id: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -2254,6 +2255,7 @@ class ExtBot(Bot, Generic[RLARGS]):
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
business_connection_id=business_connection_id,
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
)
@@ -3739,6 +3741,7 @@ class ExtBot(Bot, Generic[RLARGS]):
self,
chat_id: Union[str, int],
message_id: Optional[int] = None,
business_connection_id: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
@@ -3754,6 +3757,7 @@ class ExtBot(Bot, Generic[RLARGS]):
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
business_connection_id=business_connection_id,
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
)
@@ -4230,6 +4234,8 @@ class ExtBot(Bot, Generic[RLARGS]):
protect_content: ODVInput[bool] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
reply_markup: Optional[ReplyMarkup] = None,
business_connection_id: Optional[str] = None,
payload: Optional[str] = None,
*,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
@@ -4259,6 +4265,58 @@ class ExtBot(Bot, Generic[RLARGS]):
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
business_connection_id=business_connection_id,
payload=payload,
)
async def create_chat_subscription_invite_link(
self,
chat_id: Union[str, int],
subscription_period: int,
subscription_price: int,
name: Optional[str] = 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,
rate_limit_args: Optional[RLARGS] = None,
) -> ChatInviteLink:
return await super().create_chat_subscription_invite_link(
chat_id=chat_id,
subscription_period=subscription_period,
subscription_price=subscription_price,
name=name,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
)
async def edit_chat_subscription_invite_link(
self,
chat_id: Union[str, int],
invite_link: Union[str, "ChatInviteLink"],
name: Optional[str] = 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,
rate_limit_args: Optional[RLARGS] = None,
) -> ChatInviteLink:
return await super().edit_chat_subscription_invite_link(
chat_id=chat_id,
invite_link=invite_link,
name=name,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
)
# updated camelCase aliases
@@ -4384,4 +4442,6 @@ class ExtBot(Bot, Generic[RLARGS]):
replaceStickerInSet = replace_sticker_in_set
refundStarPayment = refund_star_payment
getStarTransactions = get_star_transactions
createChatSubscriptionInviteLink = create_chat_subscription_invite_link
editChatSubscriptionInviteLink = edit_chat_subscription_invite_link
sendPaidMedia = send_paid_media
+4 -3
View File
@@ -32,14 +32,14 @@ RT = TypeVar("RT")
UT = TypeVar("UT")
class BaseHandler(Generic[UT, CCT], ABC):
class BaseHandler(Generic[UT, CCT, RT], ABC):
"""The base class for all update handlers. Create custom handlers by inheriting from it.
Warning:
When setting :paramref:`block` to :obj:`False`, you cannot rely on adding custom
attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info.
This class is a :class:`~typing.Generic` class and accepts two type variables:
This class is a :class:`~typing.Generic` class and accepts three type variables:
1. The type of the updates that this handler will handle. Must coincide with the type of the
first argument of :paramref:`callback`. :meth:`check_update` must only accept
@@ -54,6 +54,7 @@ class BaseHandler(Generic[UT, CCT], ABC):
For this type variable, one should usually provide a :class:`~typing.TypeVar` that is
also used for the mentioned method arguments. That way, a type checker can check whether
this handler fits the definition of the :class:`~Application`.
3. The return type of the :paramref:`callback` function accepted by this handler.
.. seealso:: :wiki:`Types of Handlers <Types-of-Handlers>`
@@ -89,7 +90,7 @@ class BaseHandler(Generic[UT, CCT], ABC):
)
def __init__(
self,
self: "BaseHandler[UT, CCT, RT]",
callback: HandlerCallback[UT, CCT, RT],
block: DVType[bool] = DEFAULT_TRUE,
):
@@ -29,7 +29,7 @@ from telegram.ext._utils.types import CCT, HandlerCallback
RT = TypeVar("RT")
class BusinessConnectionHandler(BaseHandler[Update, CCT]):
class BusinessConnectionHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram
:attr:`Business Connections <telegram.Update.business_connection>`.
@@ -65,7 +65,7 @@ class BusinessConnectionHandler(BaseHandler[Update, CCT]):
)
def __init__(
self,
self: "BusinessConnectionHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
user_id: Optional[SCT[int]] = None,
username: Optional[SCT[str]] = None,
@@ -29,7 +29,7 @@ from telegram.ext._utils.types import CCT, HandlerCallback
RT = TypeVar("RT")
class BusinessMessagesDeletedHandler(BaseHandler[Update, CCT]):
class BusinessMessagesDeletedHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle
:attr:`deleted Telegram Business messages <telegram.Update.deleted_business_messages>`.
@@ -65,7 +65,7 @@ class BusinessMessagesDeletedHandler(BaseHandler[Update, CCT]):
)
def __init__(
self,
self: "BusinessMessagesDeletedHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
chat_id: Optional[SCT[int]] = None,
username: Optional[SCT[str]] = None,
+56 -20
View File
@@ -33,7 +33,7 @@ if TYPE_CHECKING:
RT = TypeVar("RT")
class CallbackQueryHandler(BaseHandler[Update, CCT]):
class CallbackQueryHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram
:attr:`callback queries <telegram.Update.callback_query>`. Optionally based on a regex.
@@ -51,6 +51,15 @@ class CallbackQueryHandler(BaseHandler[Update, CCT]):
.. versionadded:: 13.6
* If neither :paramref:`pattern` nor :paramref:`game_pattern` is set, `any`
``CallbackQuery`` will be handled. If only :paramref:`pattern` is set, queries with
:attr:`~telegram.CallbackQuery.game_short_name` will `not` be considered and vice versa.
If both patterns are set, queries with either :attr:
`~telegram.CallbackQuery.game_short_name` or :attr:`~telegram.CallbackQuery.data`
matching the defined pattern will be handled
.. versionadded:: 21.5
Warning:
When setting :paramref:`block` to :obj:`False`, you cannot rely on adding custom
attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info.
@@ -85,6 +94,13 @@ class CallbackQueryHandler(BaseHandler[Update, CCT]):
.. versionchanged:: 13.6
Added support for arbitrary callback data.
game_pattern (:obj:`str` | :func:`re.Pattern <re.compile>` | optional)
Pattern to test :attr:`telegram.CallbackQuery.game_short_name` against. If a string or
a regex pattern is passed, :func:`re.match` is used on
:attr:`telegram.CallbackQuery.game_short_name` to determine if an update should be
handled by this handler.
.. versionadded:: 21.5
block (:obj:`bool`, optional): Determines whether the return value of the callback should
be awaited before processing the next handler in
:meth:`telegram.ext.Application.process_update`. Defaults to :obj:`True`.
@@ -98,20 +114,23 @@ class CallbackQueryHandler(BaseHandler[Update, CCT]):
.. versionchanged:: 13.6
Added support for arbitrary callback data.
game_pattern (:func:`re.Pattern <re.compile>`): Optional.
Regex pattern to test :attr:`telegram.CallbackQuery.game_short_name`
block (:obj:`bool`): Determines whether the return value of the callback should be
awaited before processing the next handler in
:meth:`telegram.ext.Application.process_update`.
"""
__slots__ = ("pattern",)
__slots__ = ("game_pattern", "pattern")
def __init__(
self,
self: "CallbackQueryHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
pattern: Optional[
Union[str, Pattern[str], type, Callable[[object], Optional[bool]]]
] = None,
game_pattern: Optional[Union[str, Pattern[str]]] = None,
block: DVType[bool] = DEFAULT_TRUE,
):
super().__init__(callback, block=block)
@@ -120,13 +139,15 @@ class CallbackQueryHandler(BaseHandler[Update, CCT]):
raise TypeError(
"The `pattern` must not be a coroutine function! Use an ordinary function instead."
)
if isinstance(pattern, str):
pattern = re.compile(pattern)
if isinstance(game_pattern, str):
game_pattern = re.compile(game_pattern)
self.pattern: Optional[
Union[str, Pattern[str], type, Callable[[object], Optional[bool]]]
] = pattern
self.game_pattern: Optional[Union[str, Pattern[str]]] = game_pattern
def check_update(self, update: object) -> Optional[Union[bool, object]]:
"""Determines whether an update should be passed to this handler's :attr:`callback`.
@@ -139,22 +160,37 @@ class CallbackQueryHandler(BaseHandler[Update, CCT]):
"""
# pylint: disable=too-many-return-statements
if isinstance(update, Update) and update.callback_query:
callback_data = update.callback_query.data
if self.pattern:
if callback_data is None:
return False
if isinstance(self.pattern, type):
return isinstance(callback_data, self.pattern)
if callable(self.pattern):
return self.pattern(callback_data)
if not isinstance(callback_data, str):
return False
if match := re.match(self.pattern, callback_data):
return match
else:
return True
return None
if not (isinstance(update, Update) and update.callback_query):
return None
callback_data = update.callback_query.data
game_short_name = update.callback_query.game_short_name
if not any([self.pattern, self.game_pattern]):
return True
# we check for .data or .game_short_name from update to filter based on whats coming
# this gives xor-like behavior
if callback_data:
if not self.pattern:
return False
if isinstance(self.pattern, type):
return isinstance(callback_data, self.pattern)
if callable(self.pattern):
return self.pattern(callback_data)
if not isinstance(callback_data, str):
return False
if match := re.match(self.pattern, callback_data):
return match
elif game_short_name:
if not self.game_pattern:
return False
if match := re.match(self.game_pattern, game_short_name):
return match
else:
return True
return False
def collect_additional_context(
self,
+4 -4
View File
@@ -23,10 +23,10 @@ from typing import Final, Optional
from telegram import Update
from telegram.ext._handlers.basehandler import BaseHandler
from telegram.ext._utils._update_parsing import parse_chat_id, parse_username
from telegram.ext._utils.types import CCT, HandlerCallback
from telegram.ext._utils.types import CCT, RT, HandlerCallback
class ChatBoostHandler(BaseHandler[Update, CCT]):
class ChatBoostHandler(BaseHandler[Update, CCT, RT]):
"""
Handler class to handle Telegram updates that contain a chat boost.
@@ -84,8 +84,8 @@ class ChatBoostHandler(BaseHandler[Update, CCT]):
and :attr:`telegram.Update.removed_chat_boost`."""
def __init__(
self,
callback: HandlerCallback[Update, CCT, None],
self: "ChatBoostHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
chat_boost_types: int = CHAT_BOOST,
chat_id: Optional[int] = None,
chat_username: Optional[str] = None,
@@ -28,7 +28,7 @@ from telegram.ext._utils._update_parsing import parse_chat_id, parse_username
from telegram.ext._utils.types import CCT, HandlerCallback
class ChatJoinRequestHandler(BaseHandler[Update, CCT]):
class ChatJoinRequestHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram updates that contain
:attr:`telegram.Update.chat_join_request`.
@@ -81,7 +81,7 @@ class ChatJoinRequestHandler(BaseHandler[Update, CCT]):
)
def __init__(
self,
self: "ChatJoinRequestHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
chat_id: Optional[SCT[int]] = None,
username: Optional[SCT[str]] = None,
+2 -2
View File
@@ -29,7 +29,7 @@ from telegram.ext._utils.types import CCT, HandlerCallback
RT = TypeVar("RT")
class ChatMemberHandler(BaseHandler[Update, CCT]):
class ChatMemberHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram updates that contain a chat member update.
Warning:
@@ -87,7 +87,7 @@ class ChatMemberHandler(BaseHandler[Update, CCT]):
and :attr:`telegram.Update.chat_member`."""
def __init__(
self,
self: "ChatMemberHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
chat_member_types: int = MY_CHAT_MEMBER,
block: DVType[bool] = DEFAULT_TRUE,
@@ -32,7 +32,7 @@ if TYPE_CHECKING:
from telegram.ext import Application
class ChosenInlineResultHandler(BaseHandler[Update, CCT]):
class ChosenInlineResultHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram updates that contain
:attr:`telegram.Update.chosen_inline_result`.
@@ -76,7 +76,7 @@ class ChosenInlineResultHandler(BaseHandler[Update, CCT]):
__slots__ = ("pattern",)
def __init__(
self,
self: "ChosenInlineResultHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
block: DVType[bool] = DEFAULT_TRUE,
pattern: Optional[Union[str, Pattern[str]]] = None,
+2 -2
View File
@@ -33,7 +33,7 @@ if TYPE_CHECKING:
RT = TypeVar("RT")
class CommandHandler(BaseHandler[Update, CCT]):
class CommandHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram commands.
Commands are Telegram messages that start with ``/``, optionally followed by an ``@`` and the
@@ -118,7 +118,7 @@ class CommandHandler(BaseHandler[Update, CCT]):
__slots__ = ("commands", "filters", "has_args")
def __init__(
self,
self: "CommandHandler[CCT, RT]",
command: SCT[str],
callback: HandlerCallback[Update, CCT, RT],
filters: Optional[filters_module.BaseFilter] = None,
+13 -13
View File
@@ -55,7 +55,7 @@ from telegram.ext._utils.types import CCT, ConversationDict, ConversationKey
if TYPE_CHECKING:
from telegram.ext import Application, Job, JobQueue
_CheckUpdateType = Tuple[object, ConversationKey, BaseHandler[Update, CCT], object]
_CheckUpdateType = Tuple[object, ConversationKey, BaseHandler[Update, CCT, object], object]
_LOGGER = get_logger(__name__, class_name="ConversationHandler")
@@ -119,7 +119,7 @@ class PendingState:
return res
class ConversationHandler(BaseHandler[Update, CCT]):
class ConversationHandler(BaseHandler[Update, CCT, object]):
"""
A handler to hold a conversation with a single or multiple users through Telegram updates by
managing three collections of other handlers.
@@ -296,10 +296,10 @@ class ConversationHandler(BaseHandler[Update, CCT]):
# pylint: disable=super-init-not-called
def __init__(
self,
entry_points: List[BaseHandler[Update, CCT]],
states: Dict[object, List[BaseHandler[Update, CCT]]],
fallbacks: List[BaseHandler[Update, CCT]],
self: "ConversationHandler[CCT]",
entry_points: List[BaseHandler[Update, CCT, object]],
states: Dict[object, List[BaseHandler[Update, CCT, object]]],
fallbacks: List[BaseHandler[Update, CCT, object]],
allow_reentry: bool = False,
per_chat: bool = True,
per_user: bool = True,
@@ -324,9 +324,9 @@ class ConversationHandler(BaseHandler[Update, CCT]):
# Store the actual setting in a protected variable instead
self._block: DVType[bool] = block
self._entry_points: List[BaseHandler[Update, CCT]] = entry_points
self._states: Dict[object, List[BaseHandler[Update, CCT]]] = states
self._fallbacks: List[BaseHandler[Update, CCT]] = fallbacks
self._entry_points: List[BaseHandler[Update, CCT, object]] = entry_points
self._states: Dict[object, List[BaseHandler[Update, CCT, object]]] = states
self._fallbacks: List[BaseHandler[Update, CCT, object]] = fallbacks
self._allow_reentry: bool = allow_reentry
self._per_user: bool = per_user
@@ -359,7 +359,7 @@ class ConversationHandler(BaseHandler[Update, CCT]):
stacklevel=2,
)
all_handlers: List[BaseHandler[Update, CCT]] = []
all_handlers: List[BaseHandler[Update, CCT, object]] = []
all_handlers.extend(entry_points)
all_handlers.extend(fallbacks)
@@ -466,7 +466,7 @@ class ConversationHandler(BaseHandler[Update, CCT]):
)
@property
def entry_points(self) -> List[BaseHandler[Update, CCT]]:
def entry_points(self) -> List[BaseHandler[Update, CCT, object]]:
"""List[:class:`telegram.ext.BaseHandler`]: A list of :obj:`BaseHandler` objects that can
trigger the start of the conversation.
"""
@@ -479,7 +479,7 @@ class ConversationHandler(BaseHandler[Update, CCT]):
)
@property
def states(self) -> Dict[object, List[BaseHandler[Update, CCT]]]:
def states(self) -> Dict[object, List[BaseHandler[Update, CCT, object]]]:
"""Dict[:obj:`object`, List[:class:`telegram.ext.BaseHandler`]]: A :obj:`dict` that
defines the different states of conversation a user can be in and one or more
associated :obj:`BaseHandler` objects that should be used in that state.
@@ -491,7 +491,7 @@ class ConversationHandler(BaseHandler[Update, CCT]):
raise AttributeError("You can not assign a new value to states after initialization.")
@property
def fallbacks(self) -> List[BaseHandler[Update, CCT]]:
def fallbacks(self) -> List[BaseHandler[Update, CCT, object]]:
"""List[:class:`telegram.ext.BaseHandler`]: A list of handlers that might be used if
the user is in a conversation, but every handler for their current state returned
:obj:`False` on :meth:`check_update`.
+2 -2
View File
@@ -32,7 +32,7 @@ if TYPE_CHECKING:
RT = TypeVar("RT")
class InlineQueryHandler(BaseHandler[Update, CCT]):
class InlineQueryHandler(BaseHandler[Update, CCT, RT]):
"""
BaseHandler class to handle Telegram updates that contain a
:attr:`telegram.Update.inline_query`.
@@ -87,7 +87,7 @@ class InlineQueryHandler(BaseHandler[Update, CCT]):
__slots__ = ("chat_types", "pattern")
def __init__(
self,
self: "InlineQueryHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
pattern: Optional[Union[str, Pattern[str]]] = None,
block: DVType[bool] = DEFAULT_TRUE,
+2 -2
View File
@@ -32,7 +32,7 @@ if TYPE_CHECKING:
RT = TypeVar("RT")
class MessageHandler(BaseHandler[Update, CCT]):
class MessageHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram messages. They might contain text, media or status
updates.
@@ -75,7 +75,7 @@ class MessageHandler(BaseHandler[Update, CCT]):
__slots__ = ("filters",)
def __init__(
self,
self: "MessageHandler[CCT, RT]",
filters: Optional[filters_module.BaseFilter],
callback: HandlerCallback[Update, CCT, RT],
block: DVType[bool] = DEFAULT_TRUE,
@@ -28,7 +28,7 @@ from telegram.ext._utils._update_parsing import parse_chat_id, parse_username
from telegram.ext._utils.types import CCT, HandlerCallback
class MessageReactionHandler(BaseHandler[Update, CCT]):
class MessageReactionHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram updates that contain a message reaction.
Note:
@@ -110,7 +110,7 @@ class MessageReactionHandler(BaseHandler[Update, CCT]):
and :attr:`telegram.Update.message_reaction_count`."""
def __init__(
self,
self: "MessageReactionHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
chat_id: Optional[SCT[int]] = None,
chat_username: Optional[SCT[str]] = None,
@@ -0,0 +1,95 @@
#!/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 the PaidMediaPurchased class."""
from typing import Optional
from telegram import Update
from telegram._utils.defaultvalue import DEFAULT_TRUE
from telegram._utils.types import SCT, DVType
from telegram.ext._handlers.basehandler import BaseHandler
from telegram.ext._utils._update_parsing import parse_chat_id, parse_username
from telegram.ext._utils.types import CCT, RT, HandlerCallback
class PaidMediaPurchasedHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram
:attr:`purchased paid media <telegram.Update.purchased_paid_media>`.
.. versionadded:: 21.6
Args:
callback (:term:`coroutine function`): The callback function for this handler. Will be
called when :meth:`check_update` has determined that an update should be processed by
this handler. Callback signature::
async def callback(update: Update, context: CallbackContext)
user_id (:obj:`int` | Collection[:obj:`int`], optional): Filters requests to allow only
those which are from the specified user ID(s).
username (:obj:`str` | Collection[:obj:`str`], optional): Filters requests to allow only
those which are from the specified username(s).
block (:obj:`bool`, optional): Determines whether the return value of the callback should
be awaited before processing the next handler in
:meth:`telegram.ext.Application.process_update`. Defaults to :obj:`True`.
.. seealso:: :wiki:`Concurrency`
Attributes:
callback (:term:`coroutine function`): The callback function for this handler.
block (:obj:`bool`): Determines whether the return value of the callback should be
awaited before processing the next handler in
:meth:`telegram.ext.Application.process_update`.
"""
__slots__ = (
"_user_ids",
"_usernames",
)
def __init__(
self: "PaidMediaPurchasedHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
user_id: Optional[SCT[int]] = None,
username: Optional[SCT[str]] = None,
block: DVType[bool] = DEFAULT_TRUE,
):
super().__init__(callback, block=block)
self._user_ids = parse_chat_id(user_id)
self._usernames = parse_username(username)
def check_update(self, update: object) -> bool:
"""Determines whether an update should be passed to this handler's :attr:`callback`.
Args:
update (:class:`telegram.Update` | :obj:`object`): Incoming update.
Returns:
:obj:`bool`
"""
if not isinstance(update, Update) or not update.purchased_paid_media:
return False
if not self._user_ids and not self._usernames:
return True
if update.purchased_paid_media.from_user.id in self._user_ids:
return True
return update.purchased_paid_media.from_user.username in self._usernames
+2 -2
View File
@@ -21,10 +21,10 @@
from telegram import Update
from telegram.ext._handlers.basehandler import BaseHandler
from telegram.ext._utils.types import CCT
from telegram.ext._utils.types import CCT, RT
class PollAnswerHandler(BaseHandler[Update, CCT]):
class PollAnswerHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram updates that contain a
:attr:`poll answer <telegram.Update.poll_answer>`.
+2 -2
View File
@@ -21,10 +21,10 @@
from telegram import Update
from telegram.ext._handlers.basehandler import BaseHandler
from telegram.ext._utils.types import CCT
from telegram.ext._utils.types import CCT, RT
class PollHandler(BaseHandler[Update, CCT]):
class PollHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram updates that contain a
:attr:`poll <telegram.Update.poll>`.
@@ -31,7 +31,7 @@ from telegram.ext._utils.types import CCT, HandlerCallback
RT = TypeVar("RT")
class PreCheckoutQueryHandler(BaseHandler[Update, CCT]):
class PreCheckoutQueryHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram :attr:`telegram.Update.pre_checkout_query`.
Warning:
@@ -73,7 +73,7 @@ class PreCheckoutQueryHandler(BaseHandler[Update, CCT]):
__slots__ = ("pattern",)
def __init__(
self,
self: "PreCheckoutQueryHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
block: DVType[bool] = DEFAULT_TRUE,
pattern: Optional[Union[str, Pattern[str]]] = None,
+2 -2
View File
@@ -33,7 +33,7 @@ if TYPE_CHECKING:
RT = TypeVar("RT")
class PrefixHandler(BaseHandler[Update, CCT]):
class PrefixHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle custom prefix commands.
This is an intermediate handler between :class:`MessageHandler` and :class:`CommandHandler`.
@@ -123,7 +123,7 @@ class PrefixHandler(BaseHandler[Update, CCT]):
__slots__ = ("commands", "filters")
def __init__(
self,
self: "PrefixHandler[CCT, RT]",
prefix: SCT[str],
command: SCT[str],
callback: HandlerCallback[Update, CCT, RT],
@@ -21,10 +21,10 @@
from telegram import Update
from telegram.ext._handlers.basehandler import BaseHandler
from telegram.ext._utils.types import CCT
from telegram.ext._utils.types import CCT, RT
class ShippingQueryHandler(BaseHandler[Update, CCT]):
class ShippingQueryHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram :attr:`telegram.Update.shipping_query`.
Warning:
@@ -29,7 +29,7 @@ if TYPE_CHECKING:
from telegram.ext import Application
class StringCommandHandler(BaseHandler[str, CCT]):
class StringCommandHandler(BaseHandler[str, CCT, RT]):
"""Handler class to handle string commands. Commands are string updates that start with
``/``. The handler will add a :obj:`list` to the
:class:`CallbackContext` named :attr:`CallbackContext.args`. It will contain a list of strings,
@@ -71,7 +71,7 @@ class StringCommandHandler(BaseHandler[str, CCT]):
__slots__ = ("command",)
def __init__(
self,
self: "StringCommandHandler[CCT, RT]",
command: str,
callback: HandlerCallback[str, CCT, RT],
block: DVType[bool] = DEFAULT_TRUE,

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