mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-19 15:45:13 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e314e78d06 | |||
| 67a97ae5a7 | |||
| 9248c539d0 | |||
| 6b5e46cc08 | |||
| b3155b2e55 | |||
| ec909e62cf | |||
| 1223e851c3 | |||
| 0b352b043e | |||
| b9d2efdec5 | |||
| 8c692d1008 | |||
| 970d2ab085 | |||
| 60b439ff42 | |||
| b17b0d248d | |||
| e0f36867cc | |||
| 01f689373c | |||
| 1e05381133 | |||
| a05362c79a | |||
| fbf07bf126 | |||
| 3017bf00a4 | |||
| 374875c786 | |||
| 8f9db63f4f | |||
| 1787586902 | |||
| 9c50a38512 | |||
| 3a49372591 | |||
| e637d1733c | |||
| b89f5d6126 | |||
| 6578c76068 | |||
| a967dbe37a | |||
| af76a8485f | |||
| 8a205b10c0 | |||
| 6d70c56159 | |||
| 0913b859d7 | |||
| c3f17bb18e | |||
| 006a290b7b | |||
| 422993b8ab |
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 }}'
|
||||
|
||||
@@ -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 }}'
|
||||
@@ -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)
|
||||
@@ -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: |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
@@ -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 ",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 ---------------------------------------------
|
||||
|
||||
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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:
|
||||
@@ -1,6 +1,8 @@
|
||||
Stickers
|
||||
--------
|
||||
|
||||
The following methods and objects allow your bot to handle stickers and sticker sets.
|
||||
|
||||
.. toctree::
|
||||
:titlesonly:
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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"]
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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`"""
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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
@@ -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."""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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]:
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -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>`.
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user