mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-20 16:15:28 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ebd48903b | |||
| 1cf000c806 | |||
| 150328799a | |||
| 6d7134608f | |||
| 8266870ed7 | |||
| 519dee7e0c | |||
| 753f3727aa | |||
| 892a66d0e8 | |||
| d5e5874f96 | |||
| a5dacab8a9 | |||
| 76d8eaf0f2 | |||
| 1e4f31f1bb | |||
| 3464f24129 | |||
| 9323caf2b8 | |||
| 77c25931a9 | |||
| b75948ede4 | |||
| b0d22acedb | |||
| 35a48f82f4 | |||
| 5d73132838 | |||
| 7c23087d08 | |||
| 2d5f4a68bb | |||
| f9f1533c40 | |||
| dfb0ae3747 | |||
| 64006aa7ae | |||
| 69ddc47a6e | |||
| a2150b3751 | |||
| 79acc1ae53 | |||
| 4cdb1a0cf7 | |||
| 6319f4bae1 | |||
| d7e063dbad | |||
| 5dd7b8f1e2 | |||
| 61b87ba318 | |||
| dd592cdd7c | |||
| f57dd52100 | |||
| 16605c54d7 |
@@ -0,0 +1,5 @@
|
||||
<b>We've just released {tag}</b>.
|
||||
Thank you to everyone who contributed to this release.
|
||||
As usual, upgrade using <code>pip install -U python-telegram-bot</code>.
|
||||
|
||||
The release notes can be found <a href="https://docs.python-telegram-bot.org/en/{tag}/changelog.html">here</a>.
|
||||
@@ -0,0 +1,30 @@
|
||||
name: Create Chango Fragment
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
create-chango-fragment:
|
||||
permissions:
|
||||
# Give the default GITHUB_TOKEN write permission to commit and push the
|
||||
# added or changed files to the repository.
|
||||
contents: write
|
||||
name: create-chango-fragment
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
# Create the new fragment
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Bibo-Joshi/chango@9d6bd9d7612eca5fab2c5161687011be59baaf19 # v0.4.0
|
||||
with:
|
||||
github-token: ${{ secrets.CHANGO_PAT }}
|
||||
query-issue-types: true
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ on:
|
||||
pull_request:
|
||||
types: [opened, reopened]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
process-dependabot-prs:
|
||||
permissions:
|
||||
@@ -16,7 +18,7 @@ jobs:
|
||||
|
||||
- name: Fetch Dependabot metadata
|
||||
id: dependabot-metadata
|
||||
uses: dependabot/fetch-metadata@dbb049abf0d677abbd7f7eee0375145b417fdd34 # v2.2.0
|
||||
uses: dependabot/fetch-metadata@d7267f607e9d3fb96fc2fbe83e0af444713e90b7 # v2.3.0
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
name: Test Documentation Build
|
||||
name: Test Admonitions Generation
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- telegram/**
|
||||
- docs/**
|
||||
- .github/workflows/docs-admonitions.yml
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
test-sphinx-build:
|
||||
name: test-sphinx-build
|
||||
test-admonitions:
|
||||
name: Test Admonitions Generation
|
||||
runs-on: ${{matrix.os}}
|
||||
permissions:
|
||||
# for uploading artifacts
|
||||
actions: write
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.10']
|
||||
python-version: ['3.12']
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
@@ -22,7 +28,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
@@ -32,17 +38,4 @@ jobs:
|
||||
python -W ignore -m pip install --upgrade pip
|
||||
python -W ignore -m pip install -r requirements-dev-all.txt
|
||||
- name: Test autogeneration of admonitions
|
||||
run: pytest -v --tb=short tests/docs/admonition_inserter.py
|
||||
- name: Build docs
|
||||
run: sphinx-build docs/source docs/build/html -W --keep-going -j auto
|
||||
- name: Upload docs
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: HTML Docs
|
||||
retention-days: 7
|
||||
path: |
|
||||
# Exclude the .doctrees folder and .buildinfo file from the artifact
|
||||
# since they are not needed and add to the size
|
||||
docs/build/html/*
|
||||
!docs/build/html/.doctrees
|
||||
!docs/build/html/.buildinfo
|
||||
run: pytest -v --tb=short tests/docs/admonition_inserter.py
|
||||
@@ -7,13 +7,15 @@ on:
|
||||
paths:
|
||||
- .github/workflows/docs-linkcheck.yml
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
test-sphinx-build:
|
||||
name: test-sphinx-linkcheck
|
||||
runs-on: ${{matrix.os}}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.10']
|
||||
python-version: ['3.12']
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
@@ -21,7 +23,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
@@ -30,3 +32,10 @@ jobs:
|
||||
python -W ignore -m pip install -r requirements-dev-all.txt
|
||||
- name: Check Links
|
||||
run: sphinx-build docs/source docs/build/html -W --keep-going -j auto -b linkcheck
|
||||
- name: Upload linkcheck output
|
||||
# Run also if the previous steps failed
|
||||
if: always()
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: linkcheck-output
|
||||
path: docs/build/html/output.*
|
||||
|
||||
@@ -6,6 +6,8 @@ on:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
zizmor:
|
||||
name: Security Analysis with zizmor
|
||||
@@ -19,13 +21,13 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@887a942a15af3a7626099df99e897a18d9e5ab3a # v5.1.0
|
||||
uses: astral-sh/setup-uv@f94ec6bedd8674c4426838e6b50417d36b6ab231 # v5.3.1
|
||||
- name: Run zizmor
|
||||
run: uvx zizmor --persona=pedantic --format sarif . > results.sarif
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
|
||||
uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor
|
||||
@@ -4,6 +4,8 @@ on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
pre-commit-ci:
|
||||
permissions:
|
||||
@@ -11,7 +13,7 @@ jobs:
|
||||
pull-requests: write # for srvaroa/labeler to add labels in PR
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: srvaroa/labeler@fe4b1c73bb8abf2f14a44a6912a8b4fee835d631 # v1.12.0
|
||||
- uses: srvaroa/labeler@0a20eccb8c94a1ee0bed5f16859aece1c45c3e55 # v1.13.0
|
||||
# Config file at .github/labeler.yml
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
@@ -4,9 +4,15 @@ on:
|
||||
schedule:
|
||||
- cron: '8 4 * * *'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# For locking the threads
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
|
||||
with:
|
||||
|
||||
@@ -4,19 +4,24 @@ on:
|
||||
# manually trigger the workflow
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Distribution
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
TAG: ${{ steps.get_tag.outputs.TAG }}
|
||||
permissions:
|
||||
# for uploading artifacts
|
||||
actions: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Install pypa/build
|
||||
@@ -46,6 +51,7 @@ jobs:
|
||||
url: https://pypi.org/p/python-telegram-bot
|
||||
permissions:
|
||||
id-token: write # IMPORTANT: mandatory for trusted publishing
|
||||
actions: read # for downloading artifacts
|
||||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
@@ -54,7 +60,7 @@ jobs:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.3
|
||||
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
|
||||
|
||||
compute-signatures:
|
||||
name: Compute SHA1 Sums and Sign with Sigstore
|
||||
@@ -64,6 +70,7 @@ jobs:
|
||||
|
||||
permissions:
|
||||
id-token: write # IMPORTANT: mandatory for sigstore
|
||||
actions: write # for up/downloading artifacts
|
||||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
@@ -100,6 +107,7 @@ jobs:
|
||||
|
||||
permissions:
|
||||
contents: write # IMPORTANT: mandatory for making GitHub Releases
|
||||
actions: read # for downloading artifacts
|
||||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
@@ -111,13 +119,14 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
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.
|
||||
# Create a tag and a GitHub Release. The description is filled by the static template, we
|
||||
# just insert the correct tag in the template.
|
||||
run: >-
|
||||
sed "s/{tag}/$TAG/g" .github/workflows/assets/release_template.html |
|
||||
gh release create
|
||||
"$TAG"
|
||||
--repo '${{ github.repository }}'
|
||||
--generate-notes
|
||||
--notes-file -
|
||||
- name: Upload artifact signatures to GitHub Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
@@ -129,3 +138,27 @@ jobs:
|
||||
gh release upload
|
||||
"$TAG" dist/**
|
||||
--repo '${{ github.repository }}'
|
||||
|
||||
telegram-channel:
|
||||
name: Publish to Telegram Channel
|
||||
needs:
|
||||
- github-release
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: release_pypi
|
||||
permissions: {}
|
||||
|
||||
steps:
|
||||
- name: Publish to Telegram Channel
|
||||
env:
|
||||
TAG: ${{ needs.build.outputs.TAG }}
|
||||
# This secret is configured only for the `pypi-release` branch
|
||||
BOT_TOKEN: ${{ secrets.CHANNEL_BOT_TOKEN }}
|
||||
run: >-
|
||||
sed "s/{tag}/$TAG/g" .github/workflows/assets/release_template.html |
|
||||
curl
|
||||
-X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage"
|
||||
-d "chat_id=@pythontelegrambotchannel"
|
||||
-d "parse_mode=HTML"
|
||||
--data-urlencode "text@-"
|
||||
|
||||
@@ -4,19 +4,24 @@ on:
|
||||
# manually trigger the workflow
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Distribution
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
TAG: ${{ steps.get_tag.outputs.TAG }}
|
||||
permissions:
|
||||
# for uploading artifacts
|
||||
actions: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Install pypa/build
|
||||
@@ -46,6 +51,7 @@ jobs:
|
||||
url: https://test.pypi.org/p/python-telegram-bot
|
||||
permissions:
|
||||
id-token: write # IMPORTANT: mandatory for trusted publishing
|
||||
actions: read # for downloading artifacts
|
||||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
@@ -54,7 +60,7 @@ jobs:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
- name: Publish to Test PyPI
|
||||
uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.3
|
||||
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
|
||||
with:
|
||||
repository-url: https://test.pypi.org/legacy/
|
||||
|
||||
@@ -66,6 +72,7 @@ jobs:
|
||||
|
||||
permissions:
|
||||
id-token: write # IMPORTANT: mandatory for sigstore
|
||||
actions: write # for up/downloading artifacts
|
||||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
@@ -102,6 +109,7 @@ jobs:
|
||||
|
||||
permissions:
|
||||
contents: write # IMPORTANT: mandatory for making GitHub Releases
|
||||
actions: read # for downloading artifacts
|
||||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
@@ -113,14 +121,15 @@ jobs:
|
||||
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.
|
||||
# Create a tag and a GitHub Release *draft*. The description is filled by the static
|
||||
# template, we just insert the correct tag in the template.
|
||||
run: >-
|
||||
sed "s/{tag}/$TAG/g" .github/workflows/assets/release_template.html |
|
||||
gh release create
|
||||
"$TAG"
|
||||
--repo '${{ github.repository }}'
|
||||
--generate-notes
|
||||
--draft
|
||||
--notes-file -
|
||||
- name: Upload artifact signatures to GitHub Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
@@ -3,11 +3,16 @@ on:
|
||||
schedule:
|
||||
- cron: '42 2 * * *'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# For adding labels and closing
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
|
||||
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
|
||||
with:
|
||||
# PRs never get stale
|
||||
days-before-stale: 3
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
# Run monday and friday morning at 03:07 - odd time to spread load on GitHub Actions
|
||||
- cron: '7 3 * * 1,5'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
check-conformity:
|
||||
name: check-conformity
|
||||
@@ -25,7 +27,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
|
||||
@@ -9,6 +9,8 @@ on:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
test-type-completeness:
|
||||
name: test-type-completeness
|
||||
|
||||
@@ -4,6 +4,8 @@ on:
|
||||
# Run first friday of the month at 03:17 - odd time to spread load on GitHub Actions
|
||||
- cron: '17 3 1-7 * 5'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
test-type-completeness:
|
||||
name: test-type-completeness
|
||||
|
||||
@@ -14,6 +14,8 @@ on:
|
||||
# Run monday and friday morning at 03:07 - odd time to spread load on GitHub Actions
|
||||
- cron: '7 3 * * 1,5'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
pytest:
|
||||
name: pytest
|
||||
@@ -28,7 +30,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
@@ -97,7 +99,7 @@ jobs:
|
||||
fail_ci_if_error: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
- name: Upload test results to Codecov
|
||||
uses: codecov/test-results-action@9739113ad922ea0a9abb4b2c0f8bf6a4aa8ef820 # v1.0.1
|
||||
uses: codecov/test-results-action@4e79e65778be1cecd5df25e14af1eafb6df80ea9 # v1.0.2
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
files: .test_report_no_optionals_junit.xml,.test_report_optionals_junit.xml
|
||||
|
||||
@@ -67,6 +67,7 @@ docs/_build/
|
||||
# PyBuilder
|
||||
target/
|
||||
.idea/
|
||||
.run/
|
||||
|
||||
# Sublime Text 2
|
||||
*.sublime*
|
||||
|
||||
@@ -7,7 +7,7 @@ ci:
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: 'v0.5.6'
|
||||
rev: 'v0.8.6'
|
||||
hooks:
|
||||
- id: ruff
|
||||
name: ruff
|
||||
@@ -18,18 +18,18 @@ repos:
|
||||
- cachetools>=5.3.3,<5.5.0
|
||||
- aiolimiter~=1.1,<1.3
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 24.4.2
|
||||
rev: 24.10.0
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
- --diff
|
||||
- --check
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 7.1.0
|
||||
rev: 7.1.1
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: https://github.com/PyCQA/pylint
|
||||
rev: v3.3.2
|
||||
rev: v3.3.3
|
||||
hooks:
|
||||
- id: pylint
|
||||
files: ^(?!(tests|docs)).*\.py$
|
||||
@@ -41,7 +41,7 @@ repos:
|
||||
- aiolimiter~=1.1,<1.3
|
||||
- . # this basically does `pip install -e .`
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v1.10.1
|
||||
rev: v1.14.1
|
||||
hooks:
|
||||
- id: mypy
|
||||
name: mypy-ptb
|
||||
@@ -68,7 +68,7 @@ repos:
|
||||
- cachetools>=5.3.3,<5.5.0
|
||||
- . # this basically does `pip install -e .`
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.16.0
|
||||
rev: v3.19.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args:
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ python:
|
||||
install:
|
||||
- method: pip
|
||||
path: .
|
||||
- requirements: docs/requirements-docs.txt
|
||||
- requirements: requirements-dev-all.txt
|
||||
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
|
||||
+1
-1
@@ -117,7 +117,7 @@ The following wonderful people contributed directly or indirectly to this projec
|
||||
- `Rahiel Kasim <https://github.com/rahiel>`_
|
||||
- `Riko Naka <https://github.com/rikonaka>`_
|
||||
- `Rizlas <https://github.com/rizlas>`_
|
||||
- `Snehashish Biswas <https://github.com/Snehashish06>`_
|
||||
- Snehashish Biswas
|
||||
- `Sahil Sharma <https://github.com/sahilsharma811>`_
|
||||
- `Sam Mosleh <https://github.com/sam-mosleh>`_
|
||||
- `Sascha <https://github.com/saschalalala>`_
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@
|
||||
:target: https://pypi.org/project/python-telegram-bot/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-8.2-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-8.3-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 **8.2** are natively supported by this library.
|
||||
All types and methods of the Telegram Bot API **8.3** 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
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
breaking = """This release removes all functionality that was deprecated in v20.x. This is in line with our :ref:`stability policy <stability-policy>`.
|
||||
This includes the following changes:
|
||||
|
||||
- Removed ``filters.CHAT`` (all messages have an associated chat) and ``filters.StatusUpdate.USER_SHARED`` (use ``filters.StatusUpdate.USERS_SHARED`` instead).
|
||||
- Removed ``Defaults.disable_web_page_preview`` and ``Defaults.quote``. Use ``Defaults.link_preview_options`` and ``Defaults.do_quote`` instead.
|
||||
- Removed ``ApplicationBuilder.(get_updates_)proxy_url`` and ``HTTPXRequest.proxy_url``. Use ``ApplicationBuilder.(get_updates_)proxy`` and ``HTTPXRequest.proxy`` instead.
|
||||
- Removed the ``*_timeout`` arguments of ``Application.run_polling`` and ``Updater.start_webhook``. Instead, specify the values via ``ApplicationBuilder.get_updates_*_timeout``.
|
||||
- Removed ``constants.InlineQueryLimit.MIN_SWITCH_PM_TEXT_LENGTH``. Use ``constants.InlineQueryResultsButtonLimit.MAX_START_PARAMETER_LENGTH`` instead.
|
||||
- Removed the argument ``quote`` of ``Message.reply_*``. Use ``do_quote`` instead.
|
||||
- Removed the superfluous ``EncryptedPassportElement.credentials`` without replacement.
|
||||
- Changed attribute value of ``PassportFile.file_date`` from :obj:`int` to :class:`datetime.datetime`. Make sure to adjust your code accordingly.
|
||||
- Changed the attribute value of ``PassportElementErrors.file_hashes`` from :obj:`list` to :obj:`tuple`. Make sure to adjust your code accordingly.
|
||||
- Make ``BaseRequest.read_timeout`` an abstract property. If you subclass ``BaseRequest``, you need to implement this property.
|
||||
- The default value for ``write_timeout`` now defaults to ``DEFAULT_NONE`` also for bot methods that send media. Previously, it was ``20``. If you subclass ``BaseRequest``, make sure to use your desired write timeout if ``RequestData.multipart_data`` is set.
|
||||
"""
|
||||
[[pull_requests]]
|
||||
uid = "4671"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = ["4659"]
|
||||
@@ -0,0 +1,5 @@
|
||||
documentation = "Add `chango <https://chango.readthedocs.io/stable/>`_ As Changelog Management Tool"
|
||||
[[pull_requests]]
|
||||
uid = "4672"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = ["4321"]
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump github/codeql-action from 3.28.8 to 3.28.10"
|
||||
[[pull_requests]]
|
||||
uid = "4697"
|
||||
author_uid = "dependabot[bot]"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump srvaroa/labeler from 1.12.0 to 1.13.0"
|
||||
[[pull_requests]]
|
||||
uid = "4698"
|
||||
author_uid = "dependabot[bot]"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump astral-sh/setup-uv from 5.2.2 to 5.3.1"
|
||||
[[pull_requests]]
|
||||
uid = "4699"
|
||||
author_uid = "dependabot[bot]"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump Bibo-Joshi/chango from 0.3.1 to 0.3.2"
|
||||
[[pull_requests]]
|
||||
uid = "4700"
|
||||
author_uid = "dependabot[bot]"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump pypa/gh-action-pypi-publish from 1.12.3 to 1.12.4"
|
||||
[[pull_requests]]
|
||||
uid = "4701"
|
||||
author_uid = "dependabot[bot]"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump pytest from 8.3.4 to 8.3.5"
|
||||
[[pull_requests]]
|
||||
uid = "4709"
|
||||
author_uid = "dependabot[bot]"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump sphinx from 8.1.3 to 8.2.3"
|
||||
[[pull_requests]]
|
||||
uid = "4710"
|
||||
author_uid = "dependabot[bot]"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump Bibo-Joshi/chango from 0.3.2 to 0.4.0"
|
||||
[[pull_requests]]
|
||||
uid = "4712"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = []
|
||||
@@ -0,0 +1,5 @@
|
||||
internal = "Bump Version to v22.0"
|
||||
[[pull_requests]]
|
||||
uid = "4719"
|
||||
author_uid = "Bibo-Joshi"
|
||||
closes_threads = []
|
||||
@@ -1,8 +1,62 @@
|
||||
.. _ptb-changelog:
|
||||
Version 21.11.1
|
||||
===============
|
||||
|
||||
=========
|
||||
Changelog
|
||||
=========
|
||||
*Released 2025-03-01*
|
||||
|
||||
This is the technical changelog for version 21.11. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
|
||||
|
||||
Documentation Improvements
|
||||
--------------------------
|
||||
|
||||
- Fix ReadTheDocs Build (:pr:`4695`)
|
||||
|
||||
Version 21.11
|
||||
=============
|
||||
|
||||
*Released 2025-03-01*
|
||||
|
||||
This is the technical changelog for version 21.11. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
|
||||
|
||||
Major Changes and New Features
|
||||
------------------------------
|
||||
|
||||
- Full Support for Bot API 8.3 (:pr:`4676` closes :issue:`4677`, :pr:`4682` by `aelkheir <https://github.com/aelkheir>`_, :pr:`4690` by `aelkheir <https://github.com/aelkheir>`_, :pr:`4691` by `aelkheir <https://github.com/aelkheir>`_)
|
||||
- Make ``provider_token`` Argument Optional (:pr:`4689`)
|
||||
- Remove Deprecated ``InlineQueryResultArticle.hide_url`` (:pr:`4640` closes :issue:`4638`)
|
||||
- Accept ``datetime.timedelta`` Input in ``Bot`` Method Parameters (:pr:`4651`)
|
||||
- Extend Customization Support for ``Bot.base_(file_)url`` (:pr:`4632` closes :issue:`3355`)
|
||||
- Support ``allow_paid_broadcast`` in ``AIORateLimiter`` (:pr:`4627` closes :issue:`4578`)
|
||||
- Add ``BaseUpdateProcessor.current_concurrent_updates`` (:pr:`4626` closes :issue:`3984`)
|
||||
|
||||
Minor Changes and Bug Fixes
|
||||
---------------------------
|
||||
|
||||
- Add Bootstrapping Logic to ``Application.run_*`` (:pr:`4673` closes :issue:`4657`)
|
||||
- Fix a Bug in ``edit_user_star_subscription`` (:pr:`4681` by `vavasik800 <https://github.com/vavasik800>`_)
|
||||
- Simplify Handling of Empty Data in ``TelegramObject.de_json`` and Friends (:pr:`4617` closes :issue:`4614`)
|
||||
|
||||
Documentation Improvements
|
||||
--------------------------
|
||||
|
||||
- Documentation Improvements (:pr:`4641`)
|
||||
- Overhaul Admonition Insertion in Documentation (:pr:`4462` closes :issue:`4414`)
|
||||
|
||||
Internal Changes
|
||||
----------------
|
||||
|
||||
- Stabilize Linkcheck Test (:pr:`4693`)
|
||||
- Bump ``pre-commit`` Hooks to Latest Versions (:pr:`4643`)
|
||||
- Refactor Tests for ``TelegramObject`` Classes with Subclasses (:pr:`4654` closes :issue:`4652`)
|
||||
- Use Fine Grained Permissions for GitHub Actions Workflows (:pr:`4668`)
|
||||
|
||||
Dependency Updates
|
||||
------------------
|
||||
|
||||
- Bump ``actions/setup-python`` from 5.3.0 to 5.4.0 (:pr:`4665`)
|
||||
- Bump ``dependabot/fetch-metadata`` from 2.2.0 to 2.3.0 (:pr:`4666`)
|
||||
- Bump ``actions/stale`` from 9.0.0 to 9.1.0 (:pr:`4667`)
|
||||
- Bump ``astral-sh/setup-uv`` from 5.1.0 to 5.2.2 (:pr:`4664`)
|
||||
- Bump ``codecov/test-results-action`` from 1.0.1 to 1.0.2 (:pr:`4663`)
|
||||
|
||||
Version 21.10
|
||||
=============
|
||||
@@ -86,7 +140,7 @@ Major Changes
|
||||
Documentation Improvements
|
||||
--------------------------
|
||||
|
||||
- Documentation Improvements (:pr:`4565` by `Snehashish06 <https://github.com/Snehashish06>`_, :pr:`4573`)
|
||||
- Documentation Improvements (:pr:`4565` by Snehashish06, :pr:`4573`)
|
||||
|
||||
Version 21.7
|
||||
============
|
||||
@@ -0,0 +1,78 @@
|
||||
# noqa: INP001
|
||||
# pylint: disable=import-error
|
||||
"""Configuration for the chango changelog tool"""
|
||||
|
||||
from collections.abc import Collection
|
||||
from typing import Optional
|
||||
|
||||
from chango.concrete import DirectoryChanGo, DirectoryVersionScanner, HeaderVersionHistory
|
||||
from chango.concrete.sections import GitHubSectionChangeNote, Section, SectionVersionNote
|
||||
|
||||
version_scanner = DirectoryVersionScanner(base_directory=".", unreleased_directory="unreleased")
|
||||
|
||||
|
||||
class ChangoSectionChangeNote(
|
||||
GitHubSectionChangeNote.with_sections( # type: ignore[misc]
|
||||
[
|
||||
Section(uid="highlights", title="Highlights", sort_order=0),
|
||||
Section(uid="breaking", title="Breaking Changes", sort_order=1),
|
||||
Section(uid="security", title="Security Changes", sort_order=2),
|
||||
Section(uid="deprecations", title="Deprecations", sort_order=3),
|
||||
Section(uid="features", title="New Features", sort_order=4),
|
||||
Section(uid="bugfixes", title="Bug Fixes", sort_order=5),
|
||||
Section(uid="dependencies", title="Dependencies", sort_order=6),
|
||||
Section(uid="other", title="Other Changes", sort_order=7),
|
||||
Section(uid="documentation", title="Documentation", sort_order=8),
|
||||
Section(uid="internal", title="Internal Changes", sort_order=9),
|
||||
]
|
||||
)
|
||||
):
|
||||
"""Custom change note type for PTB. Mainly overrides get_sections to map labels to sections"""
|
||||
|
||||
OWNER = "python-telegram-bot"
|
||||
REPOSITORY = "python-telegram-bot"
|
||||
|
||||
@classmethod
|
||||
def get_sections(
|
||||
cls,
|
||||
labels: Collection[str],
|
||||
issue_types: Optional[Collection[str]],
|
||||
) -> set[str]:
|
||||
"""Override get_sections to have customized auto-detection of relevant sections based on
|
||||
the pull request and linked issues. Certainly not perfect in all cases, but should be a
|
||||
good start for most PRs.
|
||||
"""
|
||||
combined_labels = set(labels) | (set(issue_types or []))
|
||||
|
||||
mapping = {
|
||||
"🐛 bug": "bugfixes",
|
||||
"💡 feature": "features",
|
||||
"🧹 chore": "internal",
|
||||
"⚙️ bot-api": "features",
|
||||
"⚙️ documentation": "documentation",
|
||||
"⚙️ tests": "internal",
|
||||
"⚙️ ci-cd": "internal",
|
||||
"⚙️ security": "security",
|
||||
"⚙️ examples": "documentation",
|
||||
"⚙️ type-hinting": "other",
|
||||
"🛠 refactor": "internal",
|
||||
"🛠 breaking": "breaking",
|
||||
"⚙️ dependencies": "dependencies",
|
||||
"🔗 github-actions": "internal",
|
||||
"🛠 code-quality": "internal",
|
||||
}
|
||||
|
||||
# we want to return *all* from the mapping that are in the combined_labels
|
||||
# removing superfluous sections from the fragment is a tad easier than adding them
|
||||
found = {section for label, section in mapping.items() if label in combined_labels}
|
||||
|
||||
# if we have not found any sections, we default to "other"
|
||||
return found or {"other"}
|
||||
|
||||
|
||||
chango_instance = DirectoryChanGo(
|
||||
change_note_type=ChangoSectionChangeNote,
|
||||
version_note_type=SectionVersionNote,
|
||||
version_history_type=HeaderVersionHistory,
|
||||
scanner=version_scanner,
|
||||
)
|
||||
+201
-226
@@ -16,18 +16,55 @@
|
||||
# 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
|
||||
from collections import defaultdict
|
||||
from collections.abc import Iterator
|
||||
from typing import Any, Union
|
||||
from socket import socket
|
||||
from types import FunctionType
|
||||
from typing import Union
|
||||
|
||||
from apscheduler.job import Job as APSJob
|
||||
|
||||
import telegram
|
||||
import telegram._utils.defaultvalue
|
||||
import telegram._utils.types
|
||||
import telegram.ext
|
||||
import telegram.ext._utils.types
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
# Define the namespace for type resolution. This helps dealing with the internal imports that
|
||||
# we do in many places
|
||||
# The .copy() is important to avoid modifying the original namespace
|
||||
TG_NAMESPACE = vars(telegram).copy()
|
||||
TG_NAMESPACE.update(vars(telegram._utils.types))
|
||||
TG_NAMESPACE.update(vars(telegram._utils.defaultvalue))
|
||||
TG_NAMESPACE.update(vars(telegram.ext))
|
||||
TG_NAMESPACE.update(vars(telegram.ext._utils.types))
|
||||
TG_NAMESPACE.update(vars(telegram.ext._applicationbuilder))
|
||||
TG_NAMESPACE.update({"socket": socket, "APSJob": APSJob})
|
||||
|
||||
|
||||
def _iter_own_public_methods(cls: type) -> Iterator[tuple[str, type]]:
|
||||
class PublicMethod(typing.NamedTuple):
|
||||
name: str
|
||||
method: FunctionType
|
||||
|
||||
|
||||
def _is_inherited_method(cls: type, method_name: str) -> bool:
|
||||
"""Checks if a method is inherited from a parent class.
|
||||
Inheritance is not considered if the parent class is private.
|
||||
Recurses through all direcot or indirect parent classes.
|
||||
"""
|
||||
# The [1:] slice is used to exclude the class itself from the MRO.
|
||||
for base in cls.__mro__[1:]:
|
||||
if method_name in base.__dict__ and not base.__name__.startswith("_"):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _iter_own_public_methods(cls: type) -> Iterator[PublicMethod]:
|
||||
"""Iterates over methods of a class that are not protected/private,
|
||||
not camelCase and not inherited from the parent class.
|
||||
|
||||
@@ -35,13 +72,15 @@ def _iter_own_public_methods(cls: type) -> Iterator[tuple[str, type]]:
|
||||
|
||||
This function is defined outside the class because it is used to create class constants.
|
||||
"""
|
||||
return (
|
||||
m
|
||||
for m in inspect.getmembers(cls, predicate=inspect.isfunction) # not .ismethod
|
||||
if not m[0].startswith("_")
|
||||
and m[0].islower() # to avoid camelCase methods
|
||||
and m[0] in cls.__dict__ # method is not inherited from parent class
|
||||
)
|
||||
|
||||
# Use .isfunction() instead of .ismethod() because we want to include static methods.
|
||||
for m in inspect.getmembers(cls, predicate=inspect.isfunction):
|
||||
if (
|
||||
not m[0].startswith("_")
|
||||
and m[0].islower() # to avoid camelCase methods
|
||||
and not _is_inherited_method(cls, m[0])
|
||||
):
|
||||
yield PublicMethod(m[0], m[1])
|
||||
|
||||
|
||||
class AdmonitionInserter:
|
||||
@@ -58,18 +97,12 @@ class AdmonitionInserter:
|
||||
start and end markers.
|
||||
"""
|
||||
|
||||
FORWARD_REF_SKIP_PATTERN = re.compile(r"^ForwardRef\('DefaultValue\[\w+]'\)$")
|
||||
"""A pattern that will be used to skip known ForwardRef's that need not be resolved
|
||||
to a Telegram class, e.g.:
|
||||
ForwardRef('DefaultValue[None]')
|
||||
ForwardRef('DefaultValue[DVValueType]')
|
||||
"""
|
||||
|
||||
METHOD_NAMES_FOR_BOT_AND_APPBUILDER: typing.ClassVar[dict[type, str]] = {
|
||||
cls: tuple(m[0] for m in _iter_own_public_methods(cls)) # m[0] means we take only names
|
||||
for cls in (telegram.Bot, telegram.ext.ApplicationBuilder)
|
||||
METHOD_NAMES_FOR_BOT_APP_APPBUILDER: typing.ClassVar[dict[type, str]] = {
|
||||
cls: tuple(m.name for m in _iter_own_public_methods(cls))
|
||||
for cls in (telegram.Bot, telegram.ext.ApplicationBuilder, telegram.ext.Application)
|
||||
}
|
||||
"""A dictionary mapping Bot and ApplicationBuilder classes to their relevant methods that will
|
||||
"""A dictionary mapping Bot, Application & ApplicationBuilder classes to their relevant methods
|
||||
that will
|
||||
be mentioned in 'Returned in' and 'Use in' admonitions in other classes' docstrings.
|
||||
Methods must be public, not aliases, not inherited from TelegramObject.
|
||||
"""
|
||||
@@ -83,13 +116,20 @@ class AdmonitionInserter:
|
||||
"""Dictionary with admonitions. Contains sub-dictionaries, one per admonition type.
|
||||
Each sub-dictionary matches bot methods (for "Shortcuts") or telegram classes (for other
|
||||
admonition types) to texts of admonitions, e.g.:
|
||||
|
||||
```
|
||||
{
|
||||
"use_in": {<class 'telegram._chatinvitelink.ChatInviteLink'>:
|
||||
<"Use in" admonition for ChatInviteLink>, ...},
|
||||
"available_in": {<class 'telegram._chatinvitelink.ChatInviteLink'>:
|
||||
<"Available in" admonition">, ...},
|
||||
"returned_in": {...}
|
||||
"use_in": {
|
||||
<class 'telegram._chatinvitelink.ChatInviteLink'>:
|
||||
<"Use in" admonition for ChatInviteLink>,
|
||||
...
|
||||
},
|
||||
"available_in": {
|
||||
<class 'telegram._chatinvitelink.ChatInviteLink'>:
|
||||
<"Available in" admonition">,
|
||||
...
|
||||
},
|
||||
"returned_in": {...}
|
||||
}
|
||||
```
|
||||
"""
|
||||
@@ -128,34 +168,6 @@ class AdmonitionInserter:
|
||||
# i.e. {telegram._files.sticker.Sticker: {":attr:`telegram.Message.sticker`", ...}}
|
||||
attrs_for_class = defaultdict(set)
|
||||
|
||||
# The following regex is supposed to capture a class name in a line like this:
|
||||
# media (:obj:`str` | :class:`telegram.InputFile`): Audio file to send.
|
||||
#
|
||||
# Note that even if such typing description spans over multiple lines but each line ends
|
||||
# with a backslash (otherwise Sphinx will throw an error)
|
||||
# (e.g. EncryptedPassportElement.data), then Sphinx will combine these lines into a single
|
||||
# line automatically, and it will contain no backslash (only some extra many whitespaces
|
||||
# from the indentation).
|
||||
|
||||
attr_docstr_pattern = re.compile(
|
||||
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|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.
|
||||
r".*$", # Any number of characters, end of string (end of line)
|
||||
re.VERBOSE,
|
||||
)
|
||||
|
||||
# for properties: there is no attr name in docstring. Just check if there's a class name.
|
||||
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|obj):`~?(?P<class_name>[\w.]*)`")
|
||||
|
||||
classes_to_inspect = inspect.getmembers(telegram, inspect.isclass) + inspect.getmembers(
|
||||
telegram.ext, inspect.isclass
|
||||
)
|
||||
@@ -166,40 +178,31 @@ class AdmonitionInserter:
|
||||
# docstrings.
|
||||
name_of_inspected_class_in_docstr = self._generate_class_name_for_link(inspected_class)
|
||||
|
||||
# Parsing part of the docstring with attributes (parsing of properties follows later)
|
||||
docstring_lines = inspect.getdoc(inspected_class).splitlines()
|
||||
lines_with_attrs = []
|
||||
for idx, line in enumerate(docstring_lines):
|
||||
if line.strip() == "Attributes:":
|
||||
lines_with_attrs = docstring_lines[idx + 1 :]
|
||||
break
|
||||
# Writing to dictionary: matching the class found in the type hint
|
||||
# and its subclasses to the attribute of the class being inspected.
|
||||
# The class in the attribute typehint (or its subclass) is the key,
|
||||
# ReST link to attribute of the class currently being inspected is the value.
|
||||
|
||||
for line in lines_with_attrs:
|
||||
if not (line_match := attr_docstr_pattern.match(line)):
|
||||
continue
|
||||
|
||||
target_attr = line_match.group("attr_name")
|
||||
# a typing description of one attribute can contain multiple classes
|
||||
for match in single_class_name_pattern.finditer(line):
|
||||
name_of_class_in_attr = match.group("class_name")
|
||||
|
||||
# Writing to dictionary: matching the class found in the docstring
|
||||
# and its subclasses to the attribute of the class being inspected.
|
||||
# The class in the attribute docstring (or its subclass) is the key,
|
||||
# ReST link to attribute of the class currently being inspected is the value.
|
||||
try:
|
||||
self._resolve_arg_and_add_link(
|
||||
arg=name_of_class_in_attr,
|
||||
dict_of_methods_for_class=attrs_for_class,
|
||||
link=f":attr:`{name_of_inspected_class_in_docstr}.{target_attr}`",
|
||||
)
|
||||
except NotImplementedError as e:
|
||||
raise NotImplementedError(
|
||||
"Error generating Sphinx 'Available in' admonition "
|
||||
f"(admonition_inserter.py). Class {name_of_class_in_attr} present in "
|
||||
f"attribute {target_attr} of class {name_of_inspected_class_in_docstr}"
|
||||
f" could not be resolved. {e!s}"
|
||||
) from e
|
||||
# best effort - args of __init__ means not all attributes are covered, but there is no
|
||||
# other way to get type hints of all attributes, other than doing ast parsing maybe.
|
||||
# (Docstring parsing was discontinued with the closing of #4414)
|
||||
type_hints = typing.get_type_hints(inspected_class.__init__, localns=TG_NAMESPACE)
|
||||
class_attrs = [slot for slot in mro_slots(inspected_class) if not slot.startswith("_")]
|
||||
for target_attr in class_attrs:
|
||||
try:
|
||||
self._resolve_arg_and_add_link(
|
||||
dict_of_methods_for_class=attrs_for_class,
|
||||
link=f":attr:`{name_of_inspected_class_in_docstr}.{target_attr}`",
|
||||
type_hints={target_attr: type_hints.get(target_attr)},
|
||||
resolve_nested_type_vars=False,
|
||||
)
|
||||
except NotImplementedError as e:
|
||||
raise NotImplementedError(
|
||||
"Error generating Sphinx 'Available in' admonition "
|
||||
f"(admonition_inserter.py). Class {inspected_class} present in "
|
||||
f"attribute {target_attr} of class {name_of_inspected_class_in_docstr}"
|
||||
f" could not be resolved. {e!s}"
|
||||
) from e
|
||||
|
||||
# Properties need to be parsed separately because they act like attributes but not
|
||||
# listed as attributes.
|
||||
@@ -210,39 +213,29 @@ class AdmonitionInserter:
|
||||
if prop_name not in inspected_class.__dict__:
|
||||
continue
|
||||
|
||||
# 1. Can't use typing.get_type_hints because double-quoted type hints
|
||||
# (like "Application") will throw a NameError
|
||||
# 2. Can't use inspect.signature because return annotations of properties can be
|
||||
# hard to parse (like "(self) -> BD").
|
||||
# 3. fget is used to access the actual function under the property wrapper
|
||||
docstring = inspect.getdoc(getattr(inspected_class, prop_name).fget)
|
||||
if docstring is None:
|
||||
continue
|
||||
# fget is used to access the actual function under the property wrapper
|
||||
type_hints = typing.get_type_hints(
|
||||
getattr(inspected_class, prop_name).fget, localns=TG_NAMESPACE
|
||||
)
|
||||
|
||||
first_line = docstring.splitlines()[0]
|
||||
if not prop_docstring_pattern.match(first_line):
|
||||
continue
|
||||
|
||||
for match in single_class_name_pattern.finditer(first_line):
|
||||
name_of_class_in_prop = match.group("class_name")
|
||||
|
||||
# Writing to dictionary: matching the class found in the docstring and its
|
||||
# subclasses to the property of the class being inspected.
|
||||
# The class in the property docstring (or its subclass) is the key,
|
||||
# ReST link to property of the class currently being inspected is the value.
|
||||
try:
|
||||
self._resolve_arg_and_add_link(
|
||||
arg=name_of_class_in_prop,
|
||||
dict_of_methods_for_class=attrs_for_class,
|
||||
link=f":attr:`{name_of_inspected_class_in_docstr}.{prop_name}`",
|
||||
)
|
||||
except NotImplementedError as e:
|
||||
raise NotImplementedError(
|
||||
"Error generating Sphinx 'Available in' admonition "
|
||||
f"(admonition_inserter.py). Class {name_of_class_in_prop} present in "
|
||||
f"property {prop_name} of class {name_of_inspected_class_in_docstr}"
|
||||
f" could not be resolved. {e!s}"
|
||||
) from e
|
||||
# Writing to dictionary: matching the class found in the docstring and its
|
||||
# subclasses to the property of the class being inspected.
|
||||
# The class in the property docstring (or its subclass) is the key,
|
||||
# ReST link to property of the class currently being inspected is the value.
|
||||
try:
|
||||
self._resolve_arg_and_add_link(
|
||||
dict_of_methods_for_class=attrs_for_class,
|
||||
link=f":attr:`{name_of_inspected_class_in_docstr}.{prop_name}`",
|
||||
type_hints={prop_name: type_hints.get("return")},
|
||||
resolve_nested_type_vars=False,
|
||||
)
|
||||
except NotImplementedError as e:
|
||||
raise NotImplementedError(
|
||||
"Error generating Sphinx 'Available in' admonition "
|
||||
f"(admonition_inserter.py). Class {inspected_class} present in "
|
||||
f"property {prop_name} of class {name_of_inspected_class_in_docstr}"
|
||||
f" could not be resolved. {e!s}"
|
||||
) from e
|
||||
|
||||
return self._generate_admonitions(attrs_for_class, admonition_type="available_in")
|
||||
|
||||
@@ -250,29 +243,28 @@ class AdmonitionInserter:
|
||||
"""Creates a dictionary with 'Returned in' admonitions for classes that are returned
|
||||
in Bot's and ApplicationBuilder's methods.
|
||||
"""
|
||||
|
||||
# Generate a mapping of classes to ReST links to Bot methods which return it,
|
||||
# i.e. {<class 'telegram._message.Message'>: {:meth:`telegram.Bot.send_message`, ...}}
|
||||
methods_for_class = defaultdict(set)
|
||||
|
||||
for cls, method_names in self.METHOD_NAMES_FOR_BOT_AND_APPBUILDER.items():
|
||||
for cls, method_names in self.METHOD_NAMES_FOR_BOT_APP_APPBUILDER.items():
|
||||
for method_name in method_names:
|
||||
sig = inspect.signature(getattr(cls, method_name))
|
||||
ret_annot = sig.return_annotation
|
||||
|
||||
method_link = self._generate_link_to_method(method_name, cls)
|
||||
arg = getattr(cls, method_name)
|
||||
ret_type_hint = typing.get_type_hints(arg, localns=TG_NAMESPACE)
|
||||
|
||||
try:
|
||||
self._resolve_arg_and_add_link(
|
||||
arg=ret_annot,
|
||||
dict_of_methods_for_class=methods_for_class,
|
||||
link=method_link,
|
||||
type_hints={"return": ret_type_hint.get("return")},
|
||||
resolve_nested_type_vars=False,
|
||||
)
|
||||
except NotImplementedError as e:
|
||||
raise NotImplementedError(
|
||||
"Error generating Sphinx 'Returned in' admonition "
|
||||
f"(admonition_inserter.py). {cls}, method {method_name}. "
|
||||
f"Couldn't resolve type hint in return annotation {ret_annot}. {e!s}"
|
||||
f"Couldn't resolve type hint in return annotation {ret_type_hint}. {e!s}"
|
||||
) from e
|
||||
|
||||
return self._generate_admonitions(methods_for_class, admonition_type="returned_in")
|
||||
@@ -299,8 +291,13 @@ class AdmonitionInserter:
|
||||
# inspect methods of all telegram classes for return statements that indicate
|
||||
# that this given method is a shortcut for a Bot method
|
||||
for _class_name, cls in inspect.getmembers(telegram, predicate=inspect.isclass):
|
||||
# no need to inspect Bot's own methods, as Bot can't have shortcuts in Bot
|
||||
if not cls.__module__.startswith("telegram"):
|
||||
# For some reason inspect.getmembers() also yields some classes that are
|
||||
# imported in the namespace but not part of the telegram module.
|
||||
continue
|
||||
|
||||
if cls is telegram.Bot:
|
||||
# no need to inspect Bot's own methods, as Bot can't have shortcuts in Bot
|
||||
continue
|
||||
|
||||
for method_name, method in _iter_own_public_methods(cls):
|
||||
@@ -310,9 +307,7 @@ class AdmonitionInserter:
|
||||
continue
|
||||
|
||||
bot_method = getattr(telegram.Bot, bot_method_match.group())
|
||||
|
||||
link_to_shortcut_method = self._generate_link_to_method(method_name, cls)
|
||||
|
||||
shortcuts_for_bot_method[bot_method].add(link_to_shortcut_method)
|
||||
|
||||
return self._generate_admonitions(shortcuts_for_bot_method, admonition_type="shortcuts")
|
||||
@@ -327,26 +322,24 @@ class AdmonitionInserter:
|
||||
# {:meth:`telegram.Bot.answer_inline_query`, ...}}
|
||||
methods_for_class = defaultdict(set)
|
||||
|
||||
for cls, method_names in self.METHOD_NAMES_FOR_BOT_AND_APPBUILDER.items():
|
||||
for cls, method_names in self.METHOD_NAMES_FOR_BOT_APP_APPBUILDER.items():
|
||||
for method_name in method_names:
|
||||
method_link = self._generate_link_to_method(method_name, cls)
|
||||
|
||||
sig = inspect.signature(getattr(cls, method_name))
|
||||
parameters = sig.parameters
|
||||
|
||||
for param in parameters.values():
|
||||
try:
|
||||
self._resolve_arg_and_add_link(
|
||||
arg=param.annotation,
|
||||
dict_of_methods_for_class=methods_for_class,
|
||||
link=method_link,
|
||||
)
|
||||
except NotImplementedError as e:
|
||||
raise NotImplementedError(
|
||||
"Error generating Sphinx 'Use in' admonition "
|
||||
f"(admonition_inserter.py). {cls}, method {method_name}, parameter "
|
||||
f"{param}: Couldn't resolve type hint {param.annotation}. {e!s}"
|
||||
) from e
|
||||
arg = getattr(cls, method_name)
|
||||
param_type_hints = typing.get_type_hints(arg, localns=TG_NAMESPACE)
|
||||
param_type_hints.pop("return", None)
|
||||
try:
|
||||
self._resolve_arg_and_add_link(
|
||||
dict_of_methods_for_class=methods_for_class,
|
||||
link=method_link,
|
||||
type_hints=param_type_hints,
|
||||
)
|
||||
except NotImplementedError as e:
|
||||
raise NotImplementedError(
|
||||
"Error generating Sphinx 'Use in' admonition "
|
||||
f"(admonition_inserter.py). {cls}, method {method_name}, parameter "
|
||||
) from e
|
||||
|
||||
return self._generate_admonitions(methods_for_class, admonition_type="use_in")
|
||||
|
||||
@@ -362,7 +355,7 @@ class AdmonitionInserter:
|
||||
for idx, value in list(enumerate(lines)):
|
||||
if value.startswith(
|
||||
(
|
||||
".. seealso:",
|
||||
# ".. seealso:",
|
||||
# The docstring contains heading "Examples:", but Sphinx will have it converted
|
||||
# to ".. admonition: Examples":
|
||||
".. admonition:: Examples",
|
||||
@@ -435,12 +428,12 @@ class AdmonitionInserter:
|
||||
return admonition_for_class
|
||||
|
||||
@staticmethod
|
||||
def _generate_class_name_for_link(cls: type) -> str:
|
||||
def _generate_class_name_for_link(cls_: type) -> str:
|
||||
"""Generates class name that can be used in a ReST link."""
|
||||
|
||||
# Check for potential presence of ".ext.", we will need to keep it.
|
||||
ext = ".ext" if ".ext." in str(cls) else ""
|
||||
return f"telegram{ext}.{cls.__name__}"
|
||||
ext = ".ext" if ".ext." in str(cls_) else ""
|
||||
return f"telegram{ext}.{cls_.__name__}"
|
||||
|
||||
def _generate_link_to_method(self, method_name: str, cls: type) -> str:
|
||||
"""Generates a ReST link to a method of a telegram class."""
|
||||
@@ -448,19 +441,22 @@ class AdmonitionInserter:
|
||||
return f":meth:`{self._generate_class_name_for_link(cls)}.{method_name}`"
|
||||
|
||||
@staticmethod
|
||||
def _iter_subclasses(cls: type) -> Iterator:
|
||||
def _iter_subclasses(cls_: type) -> Iterator:
|
||||
if not hasattr(cls_, "__subclasses__") or cls_ is telegram.TelegramObject:
|
||||
return iter([])
|
||||
return (
|
||||
# exclude private classes
|
||||
c
|
||||
for c in cls.__subclasses__()
|
||||
for c in cls_.__subclasses__()
|
||||
if not str(c).split(".")[-1].startswith("_")
|
||||
)
|
||||
|
||||
def _resolve_arg_and_add_link(
|
||||
self,
|
||||
arg: Any,
|
||||
dict_of_methods_for_class: defaultdict,
|
||||
link: str,
|
||||
type_hints: dict[str, type],
|
||||
resolve_nested_type_vars: bool = True,
|
||||
) -> None:
|
||||
"""A helper method. Tries to resolve the arg into a valid class. In case of success,
|
||||
adds the link (to a method, attribute, or property) for that class' and its subclasses'
|
||||
@@ -468,7 +464,9 @@ class AdmonitionInserter:
|
||||
|
||||
**Modifies dictionary in place.**
|
||||
"""
|
||||
for cls in self._resolve_arg(arg):
|
||||
type_hints.pop("self", None)
|
||||
|
||||
for cls in self._resolve_arg(type_hints, resolve_nested_type_vars):
|
||||
# When trying to resolve an argument from args or return annotation,
|
||||
# the method _resolve_arg returns None if nothing could be resolved.
|
||||
# Also, if class was resolved correctly, "telegram" will definitely be in its str().
|
||||
@@ -480,88 +478,67 @@ class AdmonitionInserter:
|
||||
for subclass in self._iter_subclasses(cls):
|
||||
dict_of_methods_for_class[subclass].add(link)
|
||||
|
||||
def _resolve_arg(self, arg: Any) -> Iterator[Union[type, None]]:
|
||||
def _resolve_arg(
|
||||
self,
|
||||
type_hints: dict[str, type],
|
||||
resolve_nested_type_vars: bool,
|
||||
) -> list[type]:
|
||||
"""Analyzes an argument of a method and recursively yields classes that the argument
|
||||
or its sub-arguments (in cases like Union[...]) belong to, if they can be resolved to
|
||||
telegram or telegram.ext classes.
|
||||
|
||||
Args:
|
||||
type_hints: A dictionary of argument names and their types.
|
||||
resolve_nested_type_vars: If True, nested type variables (like Application[BT, …])
|
||||
will be resolved to their actual classes. If False, only the outermost type
|
||||
variable will be resolved. *Only* affects ptb classes, not built-in types.
|
||||
Useful for checking the return type of methods, where nested type variables
|
||||
are not really useful.
|
||||
|
||||
Raises `NotImplementedError`.
|
||||
"""
|
||||
|
||||
origin = typing.get_origin(arg)
|
||||
def _is_ptb_class(cls: type) -> bool:
|
||||
if not hasattr(cls, "__module__"):
|
||||
return False
|
||||
return cls.__module__.startswith("telegram")
|
||||
|
||||
if (
|
||||
origin in (collections.abc.Callable, typing.IO)
|
||||
or arg is None
|
||||
# no other check available (by type or origin) for these:
|
||||
or str(type(arg)) in ("<class 'typing._SpecialForm'>", "<class 'ellipsis'>")
|
||||
):
|
||||
pass
|
||||
# will be edited in place
|
||||
telegram_classes = set()
|
||||
|
||||
# RECURSIVE CALLS
|
||||
# for cases like Union[Sequence....
|
||||
elif origin in (
|
||||
Union,
|
||||
collections.abc.Coroutine,
|
||||
collections.abc.Sequence,
|
||||
):
|
||||
for sub_arg in typing.get_args(arg):
|
||||
yield from self._resolve_arg(sub_arg)
|
||||
def recurse_type(type_, is_recursed_from_ptb_class: bool):
|
||||
next_is_recursed_from_ptb_class = is_recursed_from_ptb_class or _is_ptb_class(type_)
|
||||
|
||||
elif isinstance(arg, typing.TypeVar):
|
||||
# gets access to the "bound=..." parameter
|
||||
yield from self._resolve_arg(arg.__bound__)
|
||||
# END RECURSIVE CALLS
|
||||
if hasattr(type_, "__origin__"): # For generic types like Union, List, etc.
|
||||
# Make sure it's not a telegram.ext generic type (e.g. ContextTypes[...])
|
||||
org = typing.get_origin(type_)
|
||||
if "telegram.ext" in str(org):
|
||||
telegram_classes.add(org)
|
||||
|
||||
elif isinstance(arg, typing.ForwardRef):
|
||||
m = self.FORWARD_REF_PATTERN.match(str(arg))
|
||||
# We're sure it's a ForwardRef, so, unless it belongs to known exceptions,
|
||||
# the class must be resolved.
|
||||
# If it isn't resolved, we'll have the program throw an exception to be sure.
|
||||
try:
|
||||
cls = self._resolve_class(m.group("class_name"))
|
||||
except AttributeError as exc:
|
||||
# skip known ForwardRef's that need not be resolved to a Telegram class
|
||||
if self.FORWARD_REF_SKIP_PATTERN.match(str(arg)):
|
||||
pass
|
||||
else:
|
||||
raise NotImplementedError(f"Could not process ForwardRef: {arg}") from exc
|
||||
else:
|
||||
yield cls
|
||||
args = typing.get_args(type_)
|
||||
for arg in args:
|
||||
recurse_type(arg, next_is_recursed_from_ptb_class)
|
||||
elif isinstance(type_, typing.TypeVar) and (
|
||||
resolve_nested_type_vars or not is_recursed_from_ptb_class
|
||||
):
|
||||
# gets access to the "bound=..." parameter
|
||||
recurse_type(type_.__bound__, next_is_recursed_from_ptb_class)
|
||||
elif inspect.isclass(type_) and "telegram" in inspect.getmodule(type_).__name__:
|
||||
telegram_classes.add(type_)
|
||||
elif isinstance(type_, typing.ForwardRef):
|
||||
# Resolving ForwardRef is not easy. https://peps.python.org/pep-0749/ will
|
||||
# hopefully make it better by introducing typing.resolve_forward_ref() in py3.14
|
||||
# but that's not there yet
|
||||
# So for now we fall back to a best effort approach of guessing if the class is
|
||||
# available in tg or tg.ext
|
||||
with contextlib.suppress(AttributeError):
|
||||
telegram_classes.add(self._resolve_class(type_.__forward_arg__))
|
||||
|
||||
# For custom generics like telegram.ext._application.Application[~BT, ~CCT, ~UD...].
|
||||
# This must come before the check for isinstance(type) because GenericAlias can also be
|
||||
# recognized as type if it belongs to <class 'types.GenericAlias'>.
|
||||
elif str(type(arg)) in (
|
||||
"<class 'typing._GenericAlias'>",
|
||||
"<class 'types.GenericAlias'>",
|
||||
"<class 'typing._LiteralGenericAlias'>",
|
||||
):
|
||||
if "telegram" in str(arg):
|
||||
# get_origin() of telegram.ext._application.Application[~BT, ~CCT, ~UD...]
|
||||
# will produce <class 'telegram.ext._application.Application'>
|
||||
yield origin
|
||||
for type_hint in type_hints.values():
|
||||
if type_hint is not None:
|
||||
recurse_type(type_hint, False)
|
||||
|
||||
elif isinstance(arg, type):
|
||||
if "telegram" in str(arg):
|
||||
yield arg
|
||||
|
||||
# For some reason "InlineQueryResult", "InputMedia" & some others are currently not
|
||||
# recognized as ForwardRefs and are identified as plain strings.
|
||||
elif isinstance(arg, str):
|
||||
# args like "ApplicationBuilder[BT, CCT, UD, CD, BD, JQ]" can be recognized as strings.
|
||||
# Remove whatever is in the square brackets because it doesn't need to be parsed.
|
||||
arg = re.sub(r"\[.+]", "", arg)
|
||||
|
||||
cls = self._resolve_class(arg)
|
||||
# Here we don't want an exception to be thrown since we're not sure it's ForwardRef
|
||||
if cls is not None:
|
||||
yield cls
|
||||
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f"Cannot process argument {arg} of type {type(arg)} (origin {origin})"
|
||||
)
|
||||
return list(telegram_classes)
|
||||
|
||||
@staticmethod
|
||||
def _resolve_class(name: str) -> Union[type, None]:
|
||||
@@ -581,16 +558,14 @@ class AdmonitionInserter:
|
||||
f"telegram.ext.{name}",
|
||||
f"telegram.ext.filters.{name}",
|
||||
):
|
||||
try:
|
||||
return eval(option)
|
||||
# NameError will be raised if trying to eval just name and it doesn't work, e.g.
|
||||
# "Name 'ApplicationBuilder' is not defined".
|
||||
# AttributeError will be raised if trying to e.g. eval f"telegram.{name}" when the
|
||||
# class denoted by `name` actually belongs to `telegram.ext`:
|
||||
# "module 'telegram' has no attribute 'ApplicationBuilder'".
|
||||
# If neither option works, this is not a PTB class.
|
||||
except (NameError, AttributeError):
|
||||
continue
|
||||
with contextlib.suppress(NameError, AttributeError):
|
||||
return eval(option)
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -47,29 +47,29 @@ keyword_args = [
|
||||
"",
|
||||
]
|
||||
|
||||
media_write_timeout_deprecation_methods = [
|
||||
"send_photo",
|
||||
media_write_timeout_change_methods = [
|
||||
"add_sticker_to_set",
|
||||
"create_new_sticker_set",
|
||||
"send_animation",
|
||||
"send_audio",
|
||||
"send_document",
|
||||
"send_media_group",
|
||||
"send_photo",
|
||||
"send_sticker",
|
||||
"send_video",
|
||||
"send_video_note",
|
||||
"send_animation",
|
||||
"send_voice",
|
||||
"send_media_group",
|
||||
"set_chat_photo",
|
||||
"upload_sticker_file",
|
||||
"add_sticker_to_set",
|
||||
"create_new_sticker_set",
|
||||
]
|
||||
media_write_timeout_deprecation = [
|
||||
media_write_timeout_change = [
|
||||
" write_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to "
|
||||
" :paramref:`telegram.request.BaseRequest.post.write_timeout`. By default, ``20`` "
|
||||
" seconds are used as write timeout."
|
||||
"",
|
||||
"",
|
||||
" .. deprecated:: 20.7",
|
||||
" In future versions, the default value will be changed to "
|
||||
" .. versionchanged:: 22.0",
|
||||
" The default value changed to "
|
||||
" :attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.",
|
||||
"",
|
||||
"",
|
||||
|
||||
@@ -32,8 +32,8 @@ from docs.auxil.kwargs_insertion import (
|
||||
find_insert_pos_for_kwargs,
|
||||
get_updates_read_timeout_addition,
|
||||
keyword_args,
|
||||
media_write_timeout_deprecation,
|
||||
media_write_timeout_deprecation_methods,
|
||||
media_write_timeout_change,
|
||||
media_write_timeout_change_methods,
|
||||
)
|
||||
from docs.auxil.link_code import LINE_NUMBERS
|
||||
|
||||
@@ -116,9 +116,9 @@ def autodoc_process_docstring(
|
||||
|
||||
if (
|
||||
"post.write_timeout`. Defaults to" in to_insert
|
||||
and method_name in media_write_timeout_deprecation_methods
|
||||
and method_name in media_write_timeout_change_methods
|
||||
):
|
||||
effective_insert: list[str] = media_write_timeout_deprecation
|
||||
effective_insert: list[str] = media_write_timeout_change
|
||||
elif get_updates and to_insert.lstrip().startswith("read_timeout"):
|
||||
effective_insert = [to_insert, *get_updates_read_timeout_addition]
|
||||
else:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
sphinx==8.1.3
|
||||
chango~=0.4.0
|
||||
sphinx==8.2.3
|
||||
furo==2024.8.6
|
||||
furo-sphinx-search @ git+https://github.com/harshil21/furo-sphinx-search@v0.2.0.1
|
||||
sphinx-paramlinks==0.6.0
|
||||
|
||||
@@ -61,5 +61,5 @@
|
||||
}
|
||||
.admonition.returned-in > ul, .admonition.available-in > ul, .admonition.use-in > ul, .admonition.shortcuts > ul {
|
||||
max-height: 200px;
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
.. include:: ../../CHANGES.rst
|
||||
.. _ptb-changelog:
|
||||
|
||||
=========
|
||||
Changelog
|
||||
=========
|
||||
|
||||
.. chango::
|
||||
|
||||
.. include:: ../../changes/LEGACY.rst
|
||||
@@ -8,6 +8,11 @@ from pathlib import Path
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
from sphinx.application import Sphinx
|
||||
|
||||
if sys.version_info < (3, 12):
|
||||
# Due to dependency on chango
|
||||
raise RuntimeError("This documentation needs at least Python 3.12")
|
||||
|
||||
|
||||
sys.path.insert(0, str(Path("../..").resolve().absolute()))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
@@ -36,6 +41,7 @@ needs_sphinx = "8.1.3"
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
"chango.sphinx_ext",
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.napoleon",
|
||||
"sphinx.ext.intersphinx",
|
||||
@@ -52,6 +58,9 @@ extensions = [
|
||||
if os.environ.get("READTHEDOCS", "") == "True":
|
||||
extensions.append("sphinx_build_compatibility.extension")
|
||||
|
||||
# Configuration for the chango sphinx directive
|
||||
chango_pyproject_toml_path = Path(__file__).parent.parent.parent
|
||||
|
||||
# For shorter links to Wiki in docstrings
|
||||
extlinks = {
|
||||
"wiki": ("https://github.com/python-telegram-bot/python-telegram-bot/wiki/%s", "%s"),
|
||||
@@ -111,6 +120,11 @@ linkcheck_ignore = [
|
||||
# Anchors are apparently inserted by GitHub dynamically, so let's skip checking them
|
||||
"https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples#",
|
||||
r"https://github\.com/python-telegram-bot/python-telegram-bot/wiki/[\w\-_,]+\#",
|
||||
# The LGPL license link regularly causes network errors for some reason
|
||||
re.escape("https://www.gnu.org/licenses/lgpl-3.0.html"),
|
||||
# The doc-fixes branch may not always exist - doesn't matter, we only link to it from the
|
||||
# contributing guide
|
||||
re.escape("https://docs.python-telegram-bot.org/en/doc-fixes"),
|
||||
]
|
||||
linkcheck_allowed_redirects = {
|
||||
# Redirects to the default version are okay
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.. _stability-policy:
|
||||
|
||||
Stability Policy
|
||||
================
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ Your bot can accept payments from Telegram users. Please see the `introduction t
|
||||
telegram.successfulpayment
|
||||
telegram.transactionpartner
|
||||
telegram.transactionpartneraffiliateprogram
|
||||
telegram.transactionpartnerchat
|
||||
telegram.transactionpartnerfragment
|
||||
telegram.transactionpartnerother
|
||||
telegram.transactionpartnertelegramads
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
TransactionPartnerChat
|
||||
======================
|
||||
|
||||
.. autoclass:: telegram.TransactionPartnerChat
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:inherited-members: TransactionPartner
|
||||
@@ -96,4 +96,8 @@
|
||||
|
||||
.. |allow_paid_broadcast| replace:: Pass True to allow up to :tg-const:`telegram.constants.FloodLimit.PAID_MESSAGES_PER_SECOND` messages per second, ignoring `broadcasting limits <https://core.telegram.org/bots/faq#how-can-i-message-all-of-my-bot-39s-subscribers-at-once>`__ for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance.
|
||||
|
||||
.. |tz-naive-dtms| replace:: For timezone naive :obj:`datetime.datetime` objects, the default timezone of the bot will be used, which is UTC unless :attr:`telegram.ext.Defaults.tzinfo` is used.
|
||||
.. |tz-naive-dtms| replace:: For timezone naive :obj:`datetime.datetime` objects, the default timezone of the bot will be used, which is UTC unless :attr:`telegram.ext.Defaults.tzinfo` is used.
|
||||
|
||||
.. |org-verify| replace:: `on behalf of the organization <https://telegram.org/verify#third-party-verification>`__
|
||||
|
||||
.. |time-period-input| replace:: :class:`datetime.timedelta` objects are accepted in addition to plain :obj:`int` values.
|
||||
|
||||
@@ -61,9 +61,9 @@ async def start_with_shipping_callback(update: Update, context: ContextTypes.DEF
|
||||
title,
|
||||
description,
|
||||
payload,
|
||||
PAYMENT_PROVIDER_TOKEN,
|
||||
currency,
|
||||
prices,
|
||||
provider_token=PAYMENT_PROVIDER_TOKEN,
|
||||
need_name=True,
|
||||
need_phone_number=True,
|
||||
need_email=True,
|
||||
@@ -90,7 +90,13 @@ async def start_without_shipping_callback(
|
||||
# optionally pass need_name=True, need_phone_number=True,
|
||||
# need_email=True, need_shipping_address=True, is_flexible=True
|
||||
await context.bot.send_invoice(
|
||||
chat_id, title, description, payload, PAYMENT_PROVIDER_TOKEN, currency, prices
|
||||
chat_id,
|
||||
title,
|
||||
description,
|
||||
payload,
|
||||
currency,
|
||||
prices,
|
||||
provider_token=PAYMENT_PROVIDER_TOKEN,
|
||||
)
|
||||
|
||||
|
||||
|
||||
+6
-1
@@ -10,7 +10,7 @@ description = "We have made you a wrapper you can't refuse"
|
||||
readme = "README.rst"
|
||||
requires-python = ">=3.9"
|
||||
license = "LGPL-3.0-only"
|
||||
license-files = { paths = ["LICENSE", "LICENSE.dual", "LICENSE.lesser"] }
|
||||
license-files = ["LICENSE", "LICENSE.dual", "LICENSE.lesser"]
|
||||
authors = [
|
||||
{ name = "Leandro Toledo", email = "devs@python-telegram-bot.org" }
|
||||
]
|
||||
@@ -105,6 +105,11 @@ search-paths = ["telegram"]
|
||||
[tool.hatch.build]
|
||||
packages = ["telegram"]
|
||||
|
||||
# CHANGO
|
||||
[tool.chango]
|
||||
sys_path = "changes"
|
||||
chango_instance = { name= "chango_instance", module = "config" }
|
||||
|
||||
# BLACK:
|
||||
[tool.black]
|
||||
line-length = 99
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
build
|
||||
|
||||
# For the test suite
|
||||
pytest==8.3.4
|
||||
pytest==8.3.5
|
||||
|
||||
# needed because pytest doesn't come with native support for coroutines as tests
|
||||
pytest-asyncio==0.21.2
|
||||
|
||||
@@ -238,6 +238,7 @@ __all__ = (
|
||||
"TextQuote",
|
||||
"TransactionPartner",
|
||||
"TransactionPartnerAffiliateProgram",
|
||||
"TransactionPartnerChat",
|
||||
"TransactionPartnerFragment",
|
||||
"TransactionPartnerOther",
|
||||
"TransactionPartnerTelegramAds",
|
||||
@@ -275,6 +276,7 @@ from telegram._payment.stars.startransactions import StarTransaction, StarTransa
|
||||
from telegram._payment.stars.transactionpartner import (
|
||||
TransactionPartner,
|
||||
TransactionPartnerAffiliateProgram,
|
||||
TransactionPartnerChat,
|
||||
TransactionPartnerFragment,
|
||||
TransactionPartnerOther,
|
||||
TransactionPartnerTelegramAds,
|
||||
|
||||
+249
-107
@@ -96,7 +96,15 @@ from telegram._utils.files import is_local_file, parse_file_input
|
||||
from telegram._utils.logging import get_logger
|
||||
from telegram._utils.repr import build_repr_with_selected_attrs
|
||||
from telegram._utils.strings import to_camel_case
|
||||
from telegram._utils.types import CorrectOptionID, FileInput, JSONDict, ODVInput, ReplyMarkup
|
||||
from telegram._utils.types import (
|
||||
BaseUrl,
|
||||
CorrectOptionID,
|
||||
FileInput,
|
||||
JSONDict,
|
||||
ODVInput,
|
||||
ReplyMarkup,
|
||||
TimePeriod,
|
||||
)
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram._webhookinfo import WebhookInfo
|
||||
from telegram.constants import InlineQueryLimit, ReactionEmoji
|
||||
@@ -104,7 +112,7 @@ from telegram.error import EndPointNotFound, InvalidToken
|
||||
from telegram.request import BaseRequest, RequestData
|
||||
from telegram.request._httpxrequest import HTTPXRequest
|
||||
from telegram.request._requestparameter import RequestParameter
|
||||
from telegram.warnings import PTBDeprecationWarning, PTBUserWarning
|
||||
from telegram.warnings import PTBUserWarning
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import (
|
||||
@@ -126,6 +134,35 @@ if TYPE_CHECKING:
|
||||
BT = TypeVar("BT", bound="Bot")
|
||||
|
||||
|
||||
# Even though we document only {token} as supported insertion, we are a bit more flexible
|
||||
# internally and support additional variants. At the very least, we don't want the insertion
|
||||
# to be case sensitive.
|
||||
_SUPPORTED_INSERTIONS = {"token", "TOKEN", "bot_token", "BOT_TOKEN", "bot-token", "BOT-TOKEN"}
|
||||
_INSERTION_STRINGS = {f"{{{insertion}}}" for insertion in _SUPPORTED_INSERTIONS}
|
||||
|
||||
|
||||
class _TokenDict(dict):
|
||||
__slots__ = ("token",)
|
||||
|
||||
# small helper to make .format_map work without knowing which exact insertion name is used
|
||||
def __init__(self, token: str):
|
||||
self.token = token
|
||||
super().__init__()
|
||||
|
||||
def __missing__(self, key: str) -> str:
|
||||
if key in _SUPPORTED_INSERTIONS:
|
||||
return self.token
|
||||
raise KeyError(f"Base URL string contains unsupported insertion: {key}")
|
||||
|
||||
|
||||
def _parse_base_url(value: BaseUrl, token: str) -> str:
|
||||
if callable(value):
|
||||
return value(token)
|
||||
if any(insertion in value for insertion in _INSERTION_STRINGS):
|
||||
return value.format_map(_TokenDict(token))
|
||||
return value + token
|
||||
|
||||
|
||||
class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
"""This object represents a Telegram Bot.
|
||||
|
||||
@@ -193,8 +230,40 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
|
||||
Args:
|
||||
token (:obj:`str`): Bot's unique authentication token.
|
||||
base_url (:obj:`str`, optional): Telegram Bot API service URL.
|
||||
base_url (:obj:`str` | Callable[[:obj:`str`], :obj:`str`], optional): Telegram Bot API
|
||||
service URL. If the string contains ``{token}``, it will be replaced with the bot's
|
||||
token. If a callable is passed, it will be called with the bot's token as the only
|
||||
argument and must return the base URL. Otherwise, the token will be appended to the
|
||||
string. Defaults to ``"https://api.telegram.org/bot"``.
|
||||
|
||||
Tip:
|
||||
Customizing the base URL can be used to run a bot against
|
||||
:wiki:`Local Bot API Server <Local-Bot-API-Server>` or using Telegrams
|
||||
`test environment \
|
||||
<https://core.telegram.org/bots/features#dedicated-test-environment>`_.
|
||||
|
||||
Example:
|
||||
``"https://api.telegram.org/bot{token}/test"``
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
Supports callable input and string formatting.
|
||||
base_file_url (:obj:`str`, optional): Telegram Bot API file URL.
|
||||
If the string contains ``{token}``, it will be replaced with the bot's
|
||||
token. If a callable is passed, it will be called with the bot's token as the only
|
||||
argument and must return the base URL. Otherwise, the token will be appended to the
|
||||
string. Defaults to ``"https://api.telegram.org/bot"``.
|
||||
|
||||
Tip:
|
||||
Customizing the base URL can be used to run a bot against
|
||||
:wiki:`Local Bot API Server <Local-Bot-API-Server>` or using Telegrams
|
||||
`test environment \
|
||||
<https://core.telegram.org/bots/features#dedicated-test-environment>`_.
|
||||
|
||||
Example:
|
||||
``"https://api.telegram.org/file/bot{token}/test"``
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
Supports callable input and string formatting.
|
||||
request (:class:`telegram.request.BaseRequest`, optional): Pre initialized
|
||||
:class:`telegram.request.BaseRequest` instances. Will be used for all bot methods
|
||||
*except* for :meth:`get_updates`. If not passed, an instance of
|
||||
@@ -239,8 +308,8 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
def __init__(
|
||||
self,
|
||||
token: str,
|
||||
base_url: str = "https://api.telegram.org/bot",
|
||||
base_file_url: str = "https://api.telegram.org/file/bot",
|
||||
base_url: BaseUrl = "https://api.telegram.org/bot",
|
||||
base_file_url: BaseUrl = "https://api.telegram.org/file/bot",
|
||||
request: Optional[BaseRequest] = None,
|
||||
get_updates_request: Optional[BaseRequest] = None,
|
||||
private_key: Optional[bytes] = None,
|
||||
@@ -252,8 +321,11 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
raise InvalidToken("You must pass the token you received from https://t.me/Botfather!")
|
||||
self._token: str = token
|
||||
|
||||
self._base_url: str = base_url + self._token
|
||||
self._base_file_url: str = base_file_url + self._token
|
||||
self._base_url: str = _parse_base_url(base_url, self._token)
|
||||
self._base_file_url: str = _parse_base_url(base_file_url, self._token)
|
||||
self._LOGGER.debug("Set Bot API URL: %s", self._base_url)
|
||||
self._LOGGER.debug("Set Bot API File URL: %s", self._base_file_url)
|
||||
|
||||
self._local_mode: bool = local_mode
|
||||
self._bot_user: Optional[User] = None
|
||||
self._private_key: Optional[bytes] = None
|
||||
@@ -264,7 +336,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
HTTPXRequest() if request is None else request,
|
||||
)
|
||||
|
||||
# this section is about issuing a warning when using HTTP/2 and connect to a self hosted
|
||||
# this section is about issuing a warning when using HTTP/2 and connect to a self-hosted
|
||||
# bot api instance, which currently only supports HTTP/1.1. Checking if a custom base url
|
||||
# is set is the best way to do that.
|
||||
|
||||
@@ -273,14 +345,14 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
if (
|
||||
isinstance(self._request[0], HTTPXRequest)
|
||||
and self._request[0].http_version == "2"
|
||||
and not base_url.startswith("https://api.telegram.org/bot")
|
||||
and not self.base_url.startswith("https://api.telegram.org/bot")
|
||||
):
|
||||
warning_string = "get_updates_request"
|
||||
|
||||
if (
|
||||
isinstance(self._request[1], HTTPXRequest)
|
||||
and self._request[1].http_version == "2"
|
||||
and not base_url.startswith("https://api.telegram.org/bot")
|
||||
and not self.base_url.startswith("https://api.telegram.org/bot")
|
||||
):
|
||||
if warning_string:
|
||||
warning_string += " and request"
|
||||
@@ -901,7 +973,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
self._bot_user = User.de_json(result, self)
|
||||
return self._bot_user # type: ignore[return-value]
|
||||
return self._bot_user
|
||||
|
||||
async def send_message(
|
||||
self,
|
||||
@@ -1146,6 +1218,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
video_start_timestamp: Optional[int] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1170,6 +1243,10 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
original message was sent (or channel username in the format ``@channelusername``).
|
||||
message_id (:obj:`int`): Message identifier in the chat specified in
|
||||
:paramref:`from_chat_id`.
|
||||
video_start_timestamp (:obj:`int`, optional): New start timestamp for the
|
||||
forwarded video in the message
|
||||
|
||||
.. versionadded:: 21.11
|
||||
disable_notification (:obj:`bool`, optional): |disable_notification|
|
||||
protect_content (:obj:`bool`, optional): |protect_content|
|
||||
|
||||
@@ -1188,6 +1265,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
"chat_id": chat_id,
|
||||
"from_chat_id": from_chat_id,
|
||||
"message_id": message_id,
|
||||
"video_start_timestamp": video_start_timestamp,
|
||||
}
|
||||
|
||||
return await self._send_message(
|
||||
@@ -1421,7 +1499,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
audio: Union[FileInput, "Audio"],
|
||||
duration: Optional[int] = None,
|
||||
duration: Optional[TimePeriod] = None,
|
||||
performer: Optional[str] = None,
|
||||
title: Optional[str] = None,
|
||||
caption: Optional[str] = None,
|
||||
@@ -1483,7 +1561,11 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|sequenceargs|
|
||||
duration (:obj:`int`, optional): Duration of sent audio in seconds.
|
||||
duration (:obj:`int` | :class:`datetime.timedelta`, optional): Duration of sent audio
|
||||
in seconds.
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
|time-period-input|
|
||||
performer (:obj:`str`, optional): Performer.
|
||||
title (:obj:`str`, optional): Track name.
|
||||
disable_notification (:obj:`bool`, optional): |disable_notification|
|
||||
@@ -1861,7 +1943,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
video: Union[FileInput, "Video"],
|
||||
duration: Optional[int] = None,
|
||||
duration: Optional[TimePeriod] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
@@ -1879,6 +1961,8 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
message_effect_id: Optional[str] = None,
|
||||
allow_paid_broadcast: Optional[bool] = None,
|
||||
show_caption_above_media: Optional[bool] = None,
|
||||
cover: Optional[FileInput] = None,
|
||||
start_timestamp: Optional[int] = None,
|
||||
*,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
@@ -1919,9 +2003,20 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
.. versionchanged:: 20.0
|
||||
File paths as input is also accepted for bots *not* running in
|
||||
:paramref:`~telegram.Bot.local_mode`.
|
||||
duration (:obj:`int`, optional): Duration of sent video in seconds.
|
||||
duration (:obj:`int` | :class:`datetime.timedelta`, optional): Duration of sent video
|
||||
in seconds.
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
|time-period-input|
|
||||
width (:obj:`int`, optional): Video width.
|
||||
height (:obj:`int`, optional): Video height.
|
||||
cover (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||
optional): Cover for the video in the message. |fileinputnopath|
|
||||
|
||||
.. versionadded:: 21.11
|
||||
start_timestamp (:obj:`int`, optional): Start timestamp for the video in the message.
|
||||
|
||||
.. versionadded:: 21.11
|
||||
caption (:obj:`str`, optional): Video caption (may also be used when resending videos
|
||||
by file_id), 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH`
|
||||
characters after entities parsing.
|
||||
@@ -2008,6 +2103,8 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
"width": width,
|
||||
"height": height,
|
||||
"supports_streaming": supports_streaming,
|
||||
"cover": self._parse_file_input(cover, attach=True) if cover else None,
|
||||
"start_timestamp": start_timestamp,
|
||||
"thumbnail": self._parse_file_input(thumbnail, attach=True) if thumbnail else None,
|
||||
"has_spoiler": has_spoiler,
|
||||
"show_caption_above_media": show_caption_above_media,
|
||||
@@ -2040,7 +2137,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
video_note: Union[FileInput, "VideoNote"],
|
||||
duration: Optional[int] = None,
|
||||
duration: Optional[TimePeriod] = None,
|
||||
length: Optional[int] = None,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
@@ -2092,7 +2189,11 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
.. versionchanged:: 20.0
|
||||
File paths as input is also accepted for bots *not* running in
|
||||
:paramref:`~telegram.Bot.local_mode`.
|
||||
duration (:obj:`int`, optional): Duration of sent video in seconds.
|
||||
duration (:obj:`int` | :class:`datetime.timedelta`, optional): Duration of sent video
|
||||
in seconds.
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
|time-period-input|
|
||||
length (:obj:`int`, optional): Video width and height, i.e. diameter of the video
|
||||
message.
|
||||
disable_notification (:obj:`bool`, optional): |disable_notification|
|
||||
@@ -2188,7 +2289,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
animation: Union[FileInput, "Animation"],
|
||||
duration: Optional[int] = None,
|
||||
duration: Optional[TimePeriod] = None,
|
||||
width: Optional[int] = None,
|
||||
height: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
@@ -2240,7 +2341,11 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
|
||||
.. versionchanged:: 13.2
|
||||
Accept :obj:`bytes` as input.
|
||||
duration (:obj:`int`, optional): Duration of sent animation in seconds.
|
||||
duration (:obj:`int` | :class:`datetime.timedelta`, optional): Duration of sent
|
||||
animation in seconds.
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
|time-period-input|
|
||||
width (:obj:`int`, optional): Animation width.
|
||||
height (:obj:`int`, optional): Animation height.
|
||||
caption (:obj:`str`, optional): Animation caption (may also be used when resending
|
||||
@@ -2359,7 +2464,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
voice: Union[FileInput, "Voice"],
|
||||
duration: Optional[int] = None,
|
||||
duration: Optional[TimePeriod] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
@@ -2420,7 +2525,11 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|sequenceargs|
|
||||
duration (:obj:`int`, optional): Duration of the voice message in seconds.
|
||||
duration (:obj:`int` | :class:`datetime.timedelta`, optional): Duration of the voice
|
||||
message in seconds.
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
|time-period-input|
|
||||
disable_notification (:obj:`bool`, optional): |disable_notification|
|
||||
protect_content (:obj:`bool`, optional): |protect_content|
|
||||
|
||||
@@ -2692,7 +2801,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
longitude: Optional[float] = None,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
live_period: Optional[int] = None,
|
||||
live_period: Optional[TimePeriod] = None,
|
||||
horizontal_accuracy: Optional[float] = None,
|
||||
heading: Optional[int] = None,
|
||||
proximity_alert_radius: Optional[int] = None,
|
||||
@@ -2725,12 +2834,16 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
horizontal_accuracy (:obj:`int`, optional): The radius of uncertainty for the location,
|
||||
measured in meters;
|
||||
0-:tg-const:`telegram.constants.LocationLimit.HORIZONTAL_ACCURACY`.
|
||||
live_period (:obj:`int`, optional): Period in seconds for which the location will be
|
||||
live_period (:obj:`int` | :class:`datetime.timedelta`, optional): Period in seconds for
|
||||
which the location will be
|
||||
updated, should be between
|
||||
:tg-const:`telegram.constants.LocationLimit.MIN_LIVE_PERIOD` and
|
||||
:tg-const:`telegram.constants.LocationLimit.MAX_LIVE_PERIOD`, or
|
||||
:tg-const:`telegram.constants.LocationLimit.LIVE_PERIOD_FOREVER` for live
|
||||
locations that can be edited indefinitely.
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
|time-period-input|
|
||||
heading (:obj:`int`, optional): For live locations, a direction in which the user is
|
||||
moving, in degrees. Must be between
|
||||
:tg-const:`telegram.constants.LocationLimit.MIN_HEADING` and
|
||||
@@ -2848,7 +2961,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
horizontal_accuracy: Optional[float] = None,
|
||||
heading: Optional[int] = None,
|
||||
proximity_alert_radius: Optional[int] = None,
|
||||
live_period: Optional[int] = None,
|
||||
live_period: Optional[TimePeriod] = None,
|
||||
business_connection_id: Optional[str] = None,
|
||||
*,
|
||||
location: Optional[Location] = None,
|
||||
@@ -2888,7 +3001,8 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
if specified.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): An object for a new
|
||||
inline keyboard.
|
||||
live_period (:obj:`int`, optional): New period in seconds during which the location
|
||||
live_period (:obj:`int` | :class:`datetime.timedelta`, optional): New period in seconds
|
||||
during which the location
|
||||
can be updated, starting from the message send date. If
|
||||
:tg-const:`telegram.constants.LocationLimit.LIVE_PERIOD_FOREVER` is specified,
|
||||
then the location can be updated forever. Otherwise, the new value must not exceed
|
||||
@@ -2897,6 +3011,9 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
remains unchanged
|
||||
|
||||
.. versionadded:: 21.2.
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
|time-period-input|
|
||||
business_connection_id (:obj:`str`, optional): |business_id_str_edit|
|
||||
|
||||
.. versionadded:: 21.4
|
||||
@@ -3552,7 +3669,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
results: Union[
|
||||
Sequence["InlineQueryResult"], Callable[[int], Optional[Sequence["InlineQueryResult"]]]
|
||||
],
|
||||
cache_time: Optional[int] = None,
|
||||
cache_time: Optional[TimePeriod] = None,
|
||||
is_personal: Optional[bool] = None,
|
||||
next_offset: Optional[str] = None,
|
||||
button: Optional[InlineQueryResultsButton] = None,
|
||||
@@ -3588,8 +3705,12 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
a callable that accepts the current page index starting from 0. It must return
|
||||
either a list of :class:`telegram.InlineQueryResult` instances or :obj:`None` if
|
||||
there are no more results.
|
||||
cache_time (:obj:`int`, optional): The maximum amount of time in seconds that the
|
||||
cache_time (:obj:`int` | :class:`datetime.timedelta`, optional): The maximum amount of
|
||||
time in seconds that the
|
||||
result of the inline query may be cached on the server. Defaults to ``300``.
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
|time-period-input|
|
||||
is_personal (:obj:`bool`, optional): Pass :obj:`True`, if results may be cached on
|
||||
the server side only for the user that sent the query. By default,
|
||||
results may be returned to any user who sends the same query.
|
||||
@@ -3689,7 +3810,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
"allow_group_chats": allow_group_chats,
|
||||
"allow_channel_chats": allow_channel_chats,
|
||||
}
|
||||
return PreparedInlineMessage.de_json( # type: ignore[return-value]
|
||||
return PreparedInlineMessage.de_json(
|
||||
await self._post(
|
||||
"savePreparedInlineMessage",
|
||||
data,
|
||||
@@ -3744,7 +3865,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
return UserProfilePhotos.de_json(result, self) # type: ignore[return-value]
|
||||
return UserProfilePhotos.de_json(result, self)
|
||||
|
||||
async def get_file(
|
||||
self,
|
||||
@@ -3809,7 +3930,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
if file_path and not is_local_file(file_path):
|
||||
result["file_path"] = f"{self._base_file_url}/{file_path}"
|
||||
|
||||
return File.de_json(result, self) # type: ignore[return-value]
|
||||
return File.de_json(result, self)
|
||||
|
||||
async def ban_chat_member(
|
||||
self,
|
||||
@@ -4005,7 +4126,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
text: Optional[str] = None,
|
||||
show_alert: Optional[bool] = None,
|
||||
url: Optional[str] = None,
|
||||
cache_time: Optional[int] = None,
|
||||
cache_time: Optional[TimePeriod] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -4036,9 +4157,13 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
opens your game - note that this will only work if the query comes from a callback
|
||||
game button. Otherwise, you may use links like t.me/your_bot?start=XXXX that open
|
||||
your bot with a parameter.
|
||||
cache_time (:obj:`int`, optional): The maximum amount of time in seconds that the
|
||||
cache_time (:obj:`int` | :class:`datetime.timedelta`, optional): The maximum amount of
|
||||
time in seconds that the
|
||||
result of the callback query may be cached client-side. Defaults to 0.
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
|time-period-input|
|
||||
|
||||
Returns:
|
||||
:obj:`bool` On success, :obj:`True` is returned.
|
||||
|
||||
@@ -4386,7 +4511,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
self,
|
||||
offset: Optional[int] = None,
|
||||
limit: Optional[int] = None,
|
||||
timeout: Optional[int] = None, # noqa: ASYNC109
|
||||
timeout: Optional[int] = None,
|
||||
allowed_updates: Optional[Sequence[str]] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -4456,19 +4581,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
if not isinstance(read_timeout, DefaultValue):
|
||||
arg_read_timeout: float = read_timeout or 0
|
||||
else:
|
||||
try:
|
||||
arg_read_timeout = self._request[0].read_timeout or 0
|
||||
except NotImplementedError:
|
||||
arg_read_timeout = 2
|
||||
self._warn(
|
||||
PTBDeprecationWarning(
|
||||
"20.7",
|
||||
f"The class {self._request[0].__class__.__name__} does not override "
|
||||
"the property `read_timeout`. Overriding this property will be mandatory "
|
||||
"in future versions. Using 2 seconds as fallback.",
|
||||
),
|
||||
stacklevel=2,
|
||||
)
|
||||
arg_read_timeout = self._request[0].read_timeout or 0
|
||||
|
||||
# Ideally we'd use an aggressive read timeout for the polling. However,
|
||||
# * Short polling should return within 2 seconds.
|
||||
@@ -4523,8 +4636,11 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
"""
|
||||
Use this method to specify a url and receive incoming updates via an outgoing webhook.
|
||||
Whenever there is an update for the bot, Telegram will send an HTTPS POST request to the
|
||||
specified url, containing An Update. In case of an unsuccessful request,
|
||||
Telegram will give up after a reasonable amount of attempts.
|
||||
specified url, containing An Update. In case of an unsuccessful request
|
||||
(a request with response
|
||||
`HTTP status code <https://en.wikipedia.org/wiki/List_of_HTTP_status_codes>`_different
|
||||
from ``2XY``),
|
||||
Telegram will repeat the request and give up after a reasonable amount of attempts.
|
||||
|
||||
If you'd like to make sure that the Webhook was set by you, you can specify secret data in
|
||||
the parameter :paramref:`secret_token`. If specified, the request will contain a header
|
||||
@@ -4729,7 +4845,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
return ChatFullInfo.de_json(result, self) # type: ignore[return-value]
|
||||
return ChatFullInfo.de_json(result, self)
|
||||
|
||||
async def get_chat_administrators(
|
||||
self,
|
||||
@@ -4842,7 +4958,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
return ChatMember.de_json(result, self) # type: ignore[return-value]
|
||||
return ChatMember.de_json(result, self)
|
||||
|
||||
async def set_chat_sticker_set(
|
||||
self,
|
||||
@@ -4937,7 +5053,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
return WebhookInfo.de_json(result, self) # type: ignore[return-value]
|
||||
return WebhookInfo.de_json(result, self)
|
||||
|
||||
async def set_game_score(
|
||||
self,
|
||||
@@ -5069,9 +5185,9 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
title: str,
|
||||
description: str,
|
||||
payload: str,
|
||||
provider_token: Optional[str], # This arg is now optional as of Bot API 7.4
|
||||
currency: str,
|
||||
prices: Sequence["LabeledPrice"],
|
||||
provider_token: Optional[str] = None,
|
||||
start_parameter: Optional[str] = None,
|
||||
photo_url: Optional[str] = None,
|
||||
photo_size: Optional[int] = None,
|
||||
@@ -5124,13 +5240,13 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
:tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`-
|
||||
:tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be
|
||||
displayed to the user, use it for your internal processes.
|
||||
provider_token (:obj:`str`): Payments provider token, obtained via
|
||||
provider_token (:obj:`str`, optional): Payments provider token, obtained via
|
||||
`@BotFather <https://t.me/BotFather>`_. Pass an empty string for payments in
|
||||
|tg_stars|.
|
||||
|
||||
.. deprecated:: 21.3
|
||||
As of Bot API 7.4, this parameter is now optional and future versions of the
|
||||
library will make it optional as well.
|
||||
.. versionchanged:: 21.11
|
||||
Bot API 7.4 made this parameter is optional and this is now reflected in the
|
||||
function signature.
|
||||
|
||||
currency (:obj:`str`): Three-letter ISO 4217 currency code, see `more on currencies
|
||||
<https://core.telegram.org/bots/payments#supported-currencies>`_. Pass ``XTR`` for
|
||||
@@ -5444,7 +5560,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
return SentWebAppMessage.de_json(api_result, self) # type: ignore[return-value]
|
||||
return SentWebAppMessage.de_json(api_result, self)
|
||||
|
||||
async def restrict_chat_member(
|
||||
self,
|
||||
@@ -5858,7 +5974,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
return ChatInviteLink.de_json(result, self) # type: ignore[return-value]
|
||||
return ChatInviteLink.de_json(result, self)
|
||||
|
||||
async def edit_chat_invite_link(
|
||||
self,
|
||||
@@ -5937,7 +6053,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
return ChatInviteLink.de_json(result, self) # type: ignore[return-value]
|
||||
return ChatInviteLink.de_json(result, self)
|
||||
|
||||
async def revoke_chat_invite_link(
|
||||
self,
|
||||
@@ -5984,7 +6100,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
return ChatInviteLink.de_json(result, self) # type: ignore[return-value]
|
||||
return ChatInviteLink.de_json(result, self)
|
||||
|
||||
async def approve_chat_join_request(
|
||||
self,
|
||||
@@ -6456,7 +6572,7 @@ class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]):
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
return StickerSet.de_json(result, self) # type: ignore[return-value]
|
||||
return StickerSet.de_json(result, self)
|
||||
|
||||
async def get_custom_emoji_stickers(
|
||||
self,
|
||||
@@ -6559,7 +6675,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
return File.de_json(result, self) # type: ignore[return-value]
|
||||
return File.de_json(result, self)
|
||||
|
||||
async def add_sticker_to_set(
|
||||
self,
|
||||
@@ -6856,7 +6972,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
:tg-const:`telegram.constants.StickerFormat.STATIC` for a
|
||||
``.WEBP`` or ``.PNG`` image, :tg-const:`telegram.constants.StickerFormat.ANIMATED`
|
||||
for a ``.TGS`` animation, :tg-const:`telegram.constants.StickerFormat.VIDEO` for a
|
||||
WEBM video.
|
||||
``.WEBM`` video.
|
||||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
@@ -6870,7 +6986,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
:tg-const:`telegram.constants.StickerSetLimit.MAX_ANIMATED_THUMBNAIL_SIZE`
|
||||
kilobytes in size; see
|
||||
`the docs <https://core.telegram.org/stickers#animation-requirements>`_ for
|
||||
animated sticker technical requirements, or a **.WEBM** video with the thumbnail up
|
||||
animated sticker technical requirements, or a ``.WEBM`` video with the thumbnail up
|
||||
to :tg-const:`telegram.constants.StickerSetLimit.MAX_ANIMATED_THUMBNAIL_SIZE`
|
||||
kilobytes in size; see
|
||||
`this <https://core.telegram.org/stickers#video-requirements>`_ for video sticker
|
||||
@@ -7190,7 +7306,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
explanation: Optional[str] = None,
|
||||
explanation_parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
open_period: Optional[int] = None,
|
||||
open_period: Optional[TimePeriod] = None,
|
||||
close_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
explanation_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -7253,10 +7369,14 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|sequenceargs|
|
||||
open_period (:obj:`int`, optional): Amount of time in seconds the poll will be active
|
||||
open_period (:obj:`int` | :class:`datetime.timedelta`, optional): Amount of time in
|
||||
seconds the poll will be active
|
||||
after creation, :tg-const:`telegram.Poll.MIN_OPEN_PERIOD`-
|
||||
:tg-const:`telegram.Poll.MAX_OPEN_PERIOD`. Can't be used together with
|
||||
:paramref:`close_date`.
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
|time-period-input|
|
||||
close_date (:obj:`int` | :obj:`datetime.datetime`, optional): Point in time (Unix
|
||||
timestamp) when the poll will be automatically closed. Must be at least
|
||||
:tg-const:`telegram.Poll.MIN_OPEN_PERIOD` and no more than
|
||||
@@ -7416,7 +7536,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
return Poll.de_json(result, self) # type: ignore[return-value]
|
||||
return Poll.de_json(result, self)
|
||||
|
||||
async def send_dice(
|
||||
self,
|
||||
@@ -7572,7 +7692,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
return ChatAdministratorRights.de_json(result, self) # type: ignore[return-value]
|
||||
return ChatAdministratorRights.de_json(result, self)
|
||||
|
||||
async def set_my_default_administrator_rights(
|
||||
self,
|
||||
@@ -7862,6 +7982,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
show_caption_above_media: Optional[bool] = None,
|
||||
allow_paid_broadcast: Optional[bool] = None,
|
||||
video_start_timestamp: Optional[int] = None,
|
||||
*,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
@@ -7881,6 +8002,10 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
from_chat_id (:obj:`int` | :obj:`str`): Unique identifier for the chat where the
|
||||
original message was sent (or channel username in the format ``@channelusername``).
|
||||
message_id (:obj:`int`): Message identifier in the chat specified in from_chat_id.
|
||||
video_start_timestamp (:obj:`int`, optional): New start timestamp for the
|
||||
copied video in the message
|
||||
|
||||
.. versionadded:: 21.11
|
||||
caption (:obj:`str`, optional): New caption for media,
|
||||
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after
|
||||
entities parsing. If not specified, the original caption is kept.
|
||||
@@ -7971,6 +8096,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
"reply_parameters": reply_parameters,
|
||||
"show_caption_above_media": show_caption_above_media,
|
||||
"allow_paid_broadcast": allow_paid_broadcast,
|
||||
"video_start_timestamp": video_start_timestamp,
|
||||
}
|
||||
|
||||
result = await self._post(
|
||||
@@ -7982,7 +8108,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
return MessageId.de_json(result, self) # type: ignore[return-value]
|
||||
return MessageId.de_json(result, self)
|
||||
|
||||
async def copy_messages(
|
||||
self,
|
||||
@@ -8133,16 +8259,16 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
return MenuButton.de_json(result, bot=self) # type: ignore[return-value]
|
||||
return MenuButton.de_json(result, bot=self)
|
||||
|
||||
async def create_invoice_link(
|
||||
self,
|
||||
title: str,
|
||||
description: str,
|
||||
payload: str,
|
||||
provider_token: Optional[str], # This arg is now optional as of Bot API 7.4
|
||||
currency: str,
|
||||
prices: Sequence["LabeledPrice"],
|
||||
provider_token: Optional[str] = None,
|
||||
max_tip_amount: Optional[int] = None,
|
||||
suggested_tip_amounts: Optional[Sequence[int]] = None,
|
||||
provider_data: Optional[Union[str, object]] = None,
|
||||
@@ -8157,7 +8283,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
send_phone_number_to_provider: Optional[bool] = None,
|
||||
send_email_to_provider: Optional[bool] = None,
|
||||
is_flexible: Optional[bool] = None,
|
||||
subscription_period: Optional[Union[int, dtm.timedelta]] = None,
|
||||
subscription_period: Optional[TimePeriod] = None,
|
||||
business_connection_id: Optional[str] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -8184,13 +8310,13 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
:tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`-
|
||||
:tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be
|
||||
displayed to the user, use it for your internal processes.
|
||||
provider_token (:obj:`str`): Payments provider token, obtained via
|
||||
provider_token (:obj:`str`, optional): Payments provider token, obtained via
|
||||
`@BotFather <https://t.me/BotFather>`_. Pass an empty string for payments in
|
||||
|tg_stars|.
|
||||
|
||||
.. deprecated:: 21.3
|
||||
As of Bot API 7.4, this parameter is now optional and future versions of the
|
||||
library will make it optional as well.
|
||||
.. versionchanged:: 21.11
|
||||
Bot API 7.4 made this parameter is optional and this is now reflected in the
|
||||
function signature.
|
||||
|
||||
currency (:obj:`str`): Three-letter ISO 4217 currency code, see `more on currencies
|
||||
<https://core.telegram.org/bots/payments#supported-currencies>`_. Pass ``XTR`` for
|
||||
@@ -8278,11 +8404,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
"is_flexible": is_flexible,
|
||||
"send_phone_number_to_provider": send_phone_number_to_provider,
|
||||
"send_email_to_provider": send_email_to_provider,
|
||||
"subscription_period": (
|
||||
subscription_period.total_seconds()
|
||||
if isinstance(subscription_period, dtm.timedelta)
|
||||
else subscription_period
|
||||
),
|
||||
"subscription_period": subscription_period,
|
||||
"business_connection_id": business_connection_id,
|
||||
}
|
||||
|
||||
@@ -8384,7 +8506,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
return ForumTopic.de_json(result, self) # type: ignore[return-value]
|
||||
return ForumTopic.de_json(result, self)
|
||||
|
||||
async def edit_forum_topic(
|
||||
self,
|
||||
@@ -8972,7 +9094,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
|
||||
"""
|
||||
data = {"language_code": language_code}
|
||||
return BotDescription.de_json( # type: ignore[return-value]
|
||||
return BotDescription.de_json(
|
||||
await self._post(
|
||||
"getMyDescription",
|
||||
data,
|
||||
@@ -9011,7 +9133,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
|
||||
"""
|
||||
data = {"language_code": language_code}
|
||||
return BotShortDescription.de_json( # type: ignore[return-value]
|
||||
return BotShortDescription.de_json(
|
||||
await self._post(
|
||||
"getMyShortDescription",
|
||||
data,
|
||||
@@ -9097,7 +9219,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
|
||||
"""
|
||||
data = {"language_code": language_code}
|
||||
return BotName.de_json( # type: ignore[return-value]
|
||||
return BotName.de_json(
|
||||
await self._post(
|
||||
"getMyName",
|
||||
data,
|
||||
@@ -9139,7 +9261,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
:class:`telegram.error.TelegramError`
|
||||
"""
|
||||
data: JSONDict = {"chat_id": chat_id, "user_id": user_id}
|
||||
return UserChatBoosts.de_json( # type: ignore[return-value]
|
||||
return UserChatBoosts.de_json(
|
||||
await self._post(
|
||||
"getUserChatBoosts",
|
||||
data,
|
||||
@@ -9166,7 +9288,8 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Use this method to change the chosen reactions on a message. Service messages can't be
|
||||
Use this method to change the chosen reactions on a message. Service messages of some types
|
||||
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. Bots can't use paid reactions.
|
||||
|
||||
@@ -9263,7 +9386,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
:class:`telegram.error.TelegramError`
|
||||
"""
|
||||
data: JSONDict = {"business_connection_id": business_connection_id}
|
||||
return BusinessConnection.de_json( # type: ignore[return-value]
|
||||
return BusinessConnection.de_json(
|
||||
await self._post(
|
||||
"getBusinessConnection",
|
||||
data,
|
||||
@@ -9402,7 +9525,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
|
||||
data: JSONDict = {"offset": offset, "limit": limit}
|
||||
|
||||
return StarTransactions.de_json( # type: ignore[return-value]
|
||||
return StarTransactions.de_json(
|
||||
await self._post(
|
||||
"getStarTransactions",
|
||||
data,
|
||||
@@ -9453,7 +9576,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
"is_canceled": is_canceled,
|
||||
}
|
||||
return await self._post(
|
||||
"editUserStartSubscription",
|
||||
"editUserStarSubscription",
|
||||
data,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
@@ -9573,7 +9696,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
async def create_chat_subscription_invite_link(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
subscription_period: int,
|
||||
subscription_period: TimePeriod,
|
||||
subscription_price: int,
|
||||
name: Optional[str] = None,
|
||||
*,
|
||||
@@ -9594,9 +9717,13 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
|
||||
subscription_period (:obj:`int`): The number of seconds the subscription will be
|
||||
subscription_period (:obj:`int` | :class:`datetime.timedelta`): 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).
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
|time-period-input|
|
||||
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`-
|
||||
@@ -9628,7 +9755,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
return ChatInviteLink.de_json(result, self) # type: ignore[return-value]
|
||||
return ChatInviteLink.de_json(result, self)
|
||||
|
||||
async def edit_chat_subscription_invite_link(
|
||||
self,
|
||||
@@ -9681,7 +9808,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
return ChatInviteLink.de_json(result, self) # type: ignore[return-value]
|
||||
return ChatInviteLink.de_json(result, self)
|
||||
|
||||
async def get_available_gifts(
|
||||
self,
|
||||
@@ -9692,7 +9819,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> Gifts:
|
||||
"""Returns the list of gifts that can be sent by the bot to users.
|
||||
"""Returns the list of gifts that can be sent by the bot to users and channel chats.
|
||||
Requires no parameters.
|
||||
|
||||
.. versionadded:: 21.8
|
||||
@@ -9703,7 +9830,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
Raises:
|
||||
:class:`telegram.error.TelegramError`
|
||||
"""
|
||||
return Gifts.de_json( # type: ignore[return-value]
|
||||
return Gifts.de_json(
|
||||
await self._post(
|
||||
"getAvailableGifts",
|
||||
read_timeout=read_timeout,
|
||||
@@ -9716,12 +9843,13 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
|
||||
async def send_gift(
|
||||
self,
|
||||
user_id: int,
|
||||
gift_id: Union[str, Gift],
|
||||
user_id: Optional[int] = None,
|
||||
gift_id: Union[str, Gift] = None, # type: ignore
|
||||
text: Optional[str] = None,
|
||||
text_parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
text_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
pay_for_upgrade: Optional[bool] = None,
|
||||
chat_id: Optional[Union[str, int]] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -9729,15 +9857,23 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> bool:
|
||||
"""Sends a gift to the given user.
|
||||
The gift can't be converted to Telegram Stars by the user
|
||||
"""Sends a gift to the given user or channel chat.
|
||||
The gift can't be converted to Telegram Stars by the receiver.
|
||||
|
||||
.. versionadded:: 21.8
|
||||
|
||||
Args:
|
||||
user_id (:obj:`int`): Unique identifier of the target user that will receive the gift
|
||||
user_id (:obj:`int`, optional): Required if :paramref:`chat_id` is not specified.
|
||||
Unique identifier of the target user that will receive the gift.
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
Now optional.
|
||||
gift_id (:obj:`str` | :class:`~telegram.Gift`): Identifier of the gift or a
|
||||
:class:`~telegram.Gift` object
|
||||
chat_id (:obj:`int` | :obj:`str`, optional): Required if :paramref:`user_id`
|
||||
is not specified. |chat_id_channel| It will receive the gift.
|
||||
|
||||
.. versionadded:: 21.11
|
||||
text (:obj:`str`, optional): Text that will be shown along with the gift;
|
||||
0- :tg-const:`telegram.constants.GiftLimit.MAX_TEXT_LENGTH` characters
|
||||
text_parse_mode (:obj:`str`, optional): Mode for parsing entities.
|
||||
@@ -9764,6 +9900,11 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
Raises:
|
||||
:class:`telegram.error.TelegramError`
|
||||
"""
|
||||
# TODO: Remove when stability policy allows, tags: deprecated 21.11
|
||||
# also we should raise a deprecation warnung if anything is passed by
|
||||
# position since it will be moved, not sure how
|
||||
if gift_id is None:
|
||||
raise TypeError("Missing required argument `gift_id`.")
|
||||
data: JSONDict = {
|
||||
"user_id": user_id,
|
||||
"gift_id": gift_id.id if isinstance(gift_id, Gift) else gift_id,
|
||||
@@ -9771,6 +9912,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
"text_parse_mode": text_parse_mode,
|
||||
"text_entities": text_entities,
|
||||
"pay_for_upgrade": pay_for_upgrade,
|
||||
"chat_id": chat_id,
|
||||
}
|
||||
return await self._post(
|
||||
"sendGift",
|
||||
@@ -9793,7 +9935,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> bool:
|
||||
"""Verifies a chat on behalf of the organization which is represented by the bot.
|
||||
"""Verifies a chat |org-verify| which is represented by the bot.
|
||||
|
||||
.. versionadded:: 21.10
|
||||
|
||||
@@ -9835,7 +9977,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> bool:
|
||||
"""Verifies a user on behalf of the organization which is represented by the bot.
|
||||
"""Verifies a user |org-verify| which is represented by the bot.
|
||||
|
||||
.. versionadded:: 21.10
|
||||
|
||||
@@ -9876,8 +10018,8 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> bool:
|
||||
"""Removes verification from a chat that is currently verified on behalf of the
|
||||
organization represented by the bot.
|
||||
"""Removes verification from a chat that is currently verified |org-verify|
|
||||
represented by the bot.
|
||||
|
||||
|
||||
|
||||
@@ -9915,8 +10057,8 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> bool:
|
||||
"""Removes verification from a user who is currently verified on behalf of the
|
||||
organization represented by the bot.
|
||||
"""Removes verification from a user who is currently verified |org-verify|
|
||||
represented by the bot.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -84,9 +84,7 @@ class BotCommandScope(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["BotCommandScope"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "BotCommandScope":
|
||||
"""Converts JSON data to the appropriate :class:`BotCommandScope` object, i.e. takes
|
||||
care of selecting the correct subclass.
|
||||
|
||||
@@ -104,9 +102,6 @@ class BotCommandScope(TelegramObject):
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[BotCommandScope]] = {
|
||||
cls.DEFAULT: BotCommandScopeDefault,
|
||||
cls.ALL_PRIVATE_CHATS: BotCommandScopeAllPrivateChats,
|
||||
|
||||
+12
-37
@@ -27,7 +27,7 @@ from telegram._files.location import Location
|
||||
from telegram._files.sticker import Sticker
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
@@ -106,20 +106,15 @@ class BusinessConnection(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["BusinessConnection"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "BusinessConnection":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
|
||||
data["date"] = from_timestamp(data.get("date"), tzinfo=loc_tzinfo)
|
||||
data["user"] = User.de_json(data.get("user"), bot)
|
||||
data["user"] = de_json_optional(data.get("user"), User, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -177,16 +172,11 @@ class BusinessMessagesDeleted(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["BusinessMessagesDeleted"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "BusinessMessagesDeleted":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["chat"] = Chat.de_json(data.get("chat"), bot)
|
||||
data["chat"] = de_json_optional(data.get("chat"), Chat, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -236,16 +226,11 @@ class BusinessIntro(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["BusinessIntro"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "BusinessIntro":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["sticker"] = Sticker.de_json(data.get("sticker"), bot)
|
||||
data["sticker"] = de_json_optional(data.get("sticker"), Sticker, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -290,16 +275,11 @@ class BusinessLocation(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["BusinessLocation"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "BusinessLocation":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["location"] = Location.de_json(data.get("location"), bot)
|
||||
data["location"] = de_json_optional(data.get("location"), Location, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -439,17 +419,12 @@ class BusinessOpeningHours(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["BusinessOpeningHours"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "BusinessOpeningHours":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["opening_hours"] = BusinessOpeningHoursInterval.de_list(
|
||||
data.get("opening_hours"), bot
|
||||
data["opening_hours"] = de_list_optional(
|
||||
data.get("opening_hours"), BusinessOpeningHoursInterval, bot
|
||||
)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -26,8 +26,9 @@ from telegram._files.location import Location
|
||||
from telegram._message import MaybeInaccessibleMessage, Message
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import JSONDict, ODVInput, ReplyMarkup
|
||||
from telegram._utils.types import JSONDict, ODVInput, ReplyMarkup, TimePeriod
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import (
|
||||
@@ -149,17 +150,12 @@ class CallbackQuery(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["CallbackQuery"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "CallbackQuery":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["from_user"] = User.de_json(data.pop("from", None), bot)
|
||||
data["message"] = Message.de_json(data.get("message"), bot)
|
||||
data["from_user"] = de_json_optional(data.pop("from", None), User, bot)
|
||||
data["message"] = de_json_optional(data.get("message"), Message, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -168,7 +164,7 @@ class CallbackQuery(TelegramObject):
|
||||
text: Optional[str] = None,
|
||||
show_alert: Optional[bool] = None,
|
||||
url: Optional[str] = None,
|
||||
cache_time: Optional[int] = None,
|
||||
cache_time: Optional[TimePeriod] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -475,7 +471,7 @@ class CallbackQuery(TelegramObject):
|
||||
horizontal_accuracy: Optional[float] = None,
|
||||
heading: Optional[int] = None,
|
||||
proximity_alert_radius: Optional[int] = None,
|
||||
live_period: Optional[int] = None,
|
||||
live_period: Optional[TimePeriod] = None,
|
||||
*,
|
||||
location: Optional[Location] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -835,6 +831,7 @@ class CallbackQuery(TelegramObject):
|
||||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
show_caption_above_media: Optional[bool] = None,
|
||||
allow_paid_broadcast: Optional[bool] = None,
|
||||
video_start_timestamp: Optional[int] = None,
|
||||
*,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
@@ -868,6 +865,7 @@ class CallbackQuery(TelegramObject):
|
||||
chat_id=chat_id,
|
||||
caption=caption,
|
||||
parse_mode=parse_mode,
|
||||
video_start_timestamp=video_start_timestamp,
|
||||
caption_entities=caption_entities,
|
||||
disable_notification=disable_notification,
|
||||
reply_to_message_id=reply_to_message_id,
|
||||
|
||||
+39
-12
@@ -31,7 +31,14 @@ from telegram._reaction import ReactionType
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils import enum
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import CorrectOptionID, FileInput, JSONDict, ODVInput, ReplyMarkup
|
||||
from telegram._utils.types import (
|
||||
CorrectOptionID,
|
||||
FileInput,
|
||||
JSONDict,
|
||||
ODVInput,
|
||||
ReplyMarkup,
|
||||
TimePeriod,
|
||||
)
|
||||
from telegram.helpers import escape_markdown
|
||||
from telegram.helpers import mention_html as helpers_mention_html
|
||||
from telegram.helpers import mention_markdown as helpers_mention_markdown
|
||||
@@ -1339,7 +1346,7 @@ class _ChatBase(TelegramObject):
|
||||
async def send_audio(
|
||||
self,
|
||||
audio: Union[FileInput, "Audio"],
|
||||
duration: Optional[int] = None,
|
||||
duration: Optional[TimePeriod] = None,
|
||||
performer: Optional[str] = None,
|
||||
title: Optional[str] = None,
|
||||
caption: Optional[str] = None,
|
||||
@@ -1569,9 +1576,9 @@ class _ChatBase(TelegramObject):
|
||||
title: str,
|
||||
description: str,
|
||||
payload: str,
|
||||
provider_token: Optional[str],
|
||||
currency: str,
|
||||
prices: Sequence["LabeledPrice"],
|
||||
provider_token: Optional[str] = None,
|
||||
start_parameter: Optional[str] = None,
|
||||
photo_url: Optional[str] = None,
|
||||
photo_size: Optional[int] = None,
|
||||
@@ -1668,7 +1675,7 @@ class _ChatBase(TelegramObject):
|
||||
longitude: Optional[float] = None,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
live_period: Optional[int] = None,
|
||||
live_period: Optional[TimePeriod] = None,
|
||||
horizontal_accuracy: Optional[float] = None,
|
||||
heading: Optional[int] = None,
|
||||
proximity_alert_radius: Optional[int] = None,
|
||||
@@ -1727,7 +1734,7 @@ class _ChatBase(TelegramObject):
|
||||
async def send_animation(
|
||||
self,
|
||||
animation: Union[FileInput, "Animation"],
|
||||
duration: Optional[int] = None,
|
||||
duration: Optional[TimePeriod] = None,
|
||||
width: Optional[int] = None,
|
||||
height: Optional[int] = None,
|
||||
caption: Optional[str] = None,
|
||||
@@ -1915,7 +1922,7 @@ class _ChatBase(TelegramObject):
|
||||
async def send_video(
|
||||
self,
|
||||
video: Union[FileInput, "Video"],
|
||||
duration: Optional[int] = None,
|
||||
duration: Optional[TimePeriod] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
@@ -1933,6 +1940,8 @@ class _ChatBase(TelegramObject):
|
||||
message_effect_id: Optional[str] = None,
|
||||
allow_paid_broadcast: Optional[bool] = None,
|
||||
show_caption_above_media: Optional[bool] = None,
|
||||
cover: Optional[FileInput] = None,
|
||||
start_timestamp: Optional[int] = None,
|
||||
*,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -1971,6 +1980,8 @@ class _ChatBase(TelegramObject):
|
||||
parse_mode=parse_mode,
|
||||
supports_streaming=supports_streaming,
|
||||
thumbnail=thumbnail,
|
||||
cover=cover,
|
||||
start_timestamp=start_timestamp,
|
||||
api_kwargs=api_kwargs,
|
||||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
caption_entities=caption_entities,
|
||||
@@ -1987,7 +1998,7 @@ class _ChatBase(TelegramObject):
|
||||
async def send_video_note(
|
||||
self,
|
||||
video_note: Union[FileInput, "VideoNote"],
|
||||
duration: Optional[int] = None,
|
||||
duration: Optional[TimePeriod] = None,
|
||||
length: Optional[int] = None,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
@@ -2045,7 +2056,7 @@ class _ChatBase(TelegramObject):
|
||||
async def send_voice(
|
||||
self,
|
||||
voice: Union[FileInput, "Voice"],
|
||||
duration: Optional[int] = None,
|
||||
duration: Optional[TimePeriod] = None,
|
||||
caption: Optional[str] = None,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
@@ -2115,7 +2126,7 @@ class _ChatBase(TelegramObject):
|
||||
reply_markup: Optional[ReplyMarkup] = None,
|
||||
explanation: Optional[str] = None,
|
||||
explanation_parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
open_period: Optional[int] = None,
|
||||
open_period: Optional[TimePeriod] = None,
|
||||
close_date: Optional[Union[int, dtm.datetime]] = None,
|
||||
explanation_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -2192,6 +2203,7 @@ class _ChatBase(TelegramObject):
|
||||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
show_caption_above_media: Optional[bool] = None,
|
||||
allow_paid_broadcast: Optional[bool] = None,
|
||||
video_start_timestamp: Optional[int] = None,
|
||||
*,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -2218,6 +2230,7 @@ class _ChatBase(TelegramObject):
|
||||
from_chat_id=from_chat_id,
|
||||
message_id=message_id,
|
||||
caption=caption,
|
||||
video_start_timestamp=video_start_timestamp,
|
||||
parse_mode=parse_mode,
|
||||
caption_entities=caption_entities,
|
||||
disable_notification=disable_notification,
|
||||
@@ -2250,6 +2263,7 @@ class _ChatBase(TelegramObject):
|
||||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
show_caption_above_media: Optional[bool] = None,
|
||||
allow_paid_broadcast: Optional[bool] = None,
|
||||
video_start_timestamp: Optional[int] = None,
|
||||
*,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -2276,6 +2290,7 @@ class _ChatBase(TelegramObject):
|
||||
chat_id=chat_id,
|
||||
message_id=message_id,
|
||||
caption=caption,
|
||||
video_start_timestamp=video_start_timestamp,
|
||||
parse_mode=parse_mode,
|
||||
caption_entities=caption_entities,
|
||||
disable_notification=disable_notification,
|
||||
@@ -2391,6 +2406,7 @@ class _ChatBase(TelegramObject):
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
video_start_timestamp: Optional[int] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2416,6 +2432,7 @@ class _ChatBase(TelegramObject):
|
||||
chat_id=self.id,
|
||||
from_chat_id=from_chat_id,
|
||||
message_id=message_id,
|
||||
video_start_timestamp=video_start_timestamp,
|
||||
disable_notification=disable_notification,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
@@ -2433,6 +2450,7 @@ class _ChatBase(TelegramObject):
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: Optional[int] = None,
|
||||
video_start_timestamp: Optional[int] = None,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -2459,6 +2477,7 @@ class _ChatBase(TelegramObject):
|
||||
from_chat_id=self.id,
|
||||
chat_id=chat_id,
|
||||
message_id=message_id,
|
||||
video_start_timestamp=video_start_timestamp,
|
||||
disable_notification=disable_notification,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
@@ -2708,7 +2727,7 @@ class _ChatBase(TelegramObject):
|
||||
|
||||
async def create_subscription_invite_link(
|
||||
self,
|
||||
subscription_period: int,
|
||||
subscription_period: TimePeriod,
|
||||
subscription_price: int,
|
||||
name: Optional[str] = None,
|
||||
*,
|
||||
@@ -3455,18 +3474,25 @@ class _ChatBase(TelegramObject):
|
||||
|
||||
await bot.send_gift(user_id=update.effective_chat.id, *args, **kwargs )
|
||||
|
||||
or::
|
||||
|
||||
await bot.send_gift(chat_id=update.effective_chat.id, *args, **kwargs )
|
||||
|
||||
For the documentation of the arguments, please see :meth:`telegram.Bot.send_gift`.
|
||||
|
||||
Caution:
|
||||
Can only work, if the chat is a private chat, see :attr:`type`.
|
||||
Will only work if the chat is a private or channel chat, see :attr:`type`.
|
||||
|
||||
.. versionadded:: 21.8
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
|
||||
Added support for channel chats.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
"""
|
||||
return await self.get_bot().send_gift(
|
||||
user_id=self.id,
|
||||
gift_id=gift_id,
|
||||
text=text,
|
||||
text_parse_mode=text_parse_mode,
|
||||
@@ -3477,6 +3503,7 @@ class _ChatBase(TelegramObject):
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
**{"chat_id" if self.type == Chat.CHANNEL else "user_id": self.id},
|
||||
)
|
||||
|
||||
async def verify(
|
||||
|
||||
@@ -24,7 +24,7 @@ from telegram import constants
|
||||
from telegram._files.document import Document
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils import enum
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.argumentparsing import de_json_optional, parse_sequence_arg
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -79,15 +79,10 @@ class BackgroundFill(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["BackgroundFill"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "BackgroundFill":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[BackgroundFill]] = {
|
||||
cls.SOLID: BackgroundFillSolid,
|
||||
cls.GRADIENT: BackgroundFillGradient,
|
||||
@@ -270,15 +265,10 @@ class BackgroundType(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["BackgroundType"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "BackgroundType":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[BackgroundType]] = {
|
||||
cls.FILL: BackgroundTypeFill,
|
||||
cls.WALLPAPER: BackgroundTypeWallpaper,
|
||||
@@ -290,10 +280,10 @@ class BackgroundType(TelegramObject):
|
||||
return _class_mapping[data.pop("type")].de_json(data=data, bot=bot)
|
||||
|
||||
if "fill" in data:
|
||||
data["fill"] = BackgroundFill.de_json(data.get("fill"), bot)
|
||||
data["fill"] = de_json_optional(data.get("fill"), BackgroundFill, bot)
|
||||
|
||||
if "document" in data:
|
||||
data["document"] = Document.de_json(data.get("document"), bot)
|
||||
data["document"] = de_json_optional(data.get("document"), Document, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -398,8 +388,8 @@ class BackgroundTypeWallpaper(BackgroundType):
|
||||
|
||||
class BackgroundTypePattern(BackgroundType):
|
||||
"""
|
||||
The background is a `PNG` or `TGV` (gzipped subset of `SVG` with `MIME` type
|
||||
`"application/x-tgwallpattern"`) pattern to be combined with the background fill
|
||||
The background is a ``.PNG`` or ``.TGV`` (gzipped subset of ``SVG`` with ``MIME`` type
|
||||
``"application/x-tgwallpattern"``) pattern to be combined with the background fill
|
||||
chosen by the user.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
@@ -533,15 +523,10 @@ class ChatBackground(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ChatBackground"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChatBackground":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["type"] = BackgroundType.de_json(data.get("type"), bot)
|
||||
data["type"] = de_json_optional(data.get("type"), BackgroundType, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
+16
-41
@@ -26,7 +26,7 @@ from telegram._chat import Chat
|
||||
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.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
@@ -110,15 +110,10 @@ class ChatBoostSource(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ChatBoostSource"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChatBoostSource":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[ChatBoostSource]] = {
|
||||
cls.PREMIUM: ChatBoostSourcePremium,
|
||||
cls.GIFT_CODE: ChatBoostSourceGiftCode,
|
||||
@@ -129,7 +124,7 @@ class ChatBoostSource(TelegramObject):
|
||||
return _class_mapping[data.pop("source")].de_json(data=data, bot=bot)
|
||||
|
||||
if "user" in data:
|
||||
data["user"] = User.de_json(data.get("user"), bot)
|
||||
data["user"] = de_json_optional(data.get("user"), User, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -290,19 +285,14 @@ class ChatBoost(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ChatBoost"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChatBoost":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["source"] = ChatBoostSource.de_json(data.get("source"), bot)
|
||||
data["source"] = de_json_optional(data.get("source"), ChatBoostSource, bot)
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
data["add_date"] = from_timestamp(data["add_date"], tzinfo=loc_tzinfo)
|
||||
data["expiration_date"] = from_timestamp(data["expiration_date"], tzinfo=loc_tzinfo)
|
||||
data["add_date"] = from_timestamp(data.get("add_date"), tzinfo=loc_tzinfo)
|
||||
data["expiration_date"] = from_timestamp(data.get("expiration_date"), tzinfo=loc_tzinfo)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -342,17 +332,12 @@ class ChatBoostUpdated(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ChatBoostUpdated"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChatBoostUpdated":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["chat"] = Chat.de_json(data.get("chat"), bot)
|
||||
data["boost"] = ChatBoost.de_json(data.get("boost"), bot)
|
||||
data["chat"] = de_json_optional(data.get("chat"), Chat, bot)
|
||||
data["boost"] = de_json_optional(data.get("boost"), ChatBoost, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -401,19 +386,14 @@ class ChatBoostRemoved(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ChatBoostRemoved"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChatBoostRemoved":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["chat"] = Chat.de_json(data.get("chat"), bot)
|
||||
data["source"] = ChatBoostSource.de_json(data.get("source"), bot)
|
||||
data["chat"] = de_json_optional(data.get("chat"), Chat, bot)
|
||||
data["source"] = de_json_optional(data.get("source"), ChatBoostSource, bot)
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
data["remove_date"] = from_timestamp(data["remove_date"], tzinfo=loc_tzinfo)
|
||||
data["remove_date"] = from_timestamp(data.get("remove_date"), tzinfo=loc_tzinfo)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -450,15 +430,10 @@ class UserChatBoosts(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["UserChatBoosts"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "UserChatBoosts":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["boosts"] = ChatBoost.de_list(data.get("boosts"), bot)
|
||||
data["boosts"] = de_list_optional(data.get("boosts"), ChatBoost, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
+26
-18
@@ -28,7 +28,7 @@ from telegram._chatlocation import ChatLocation
|
||||
from telegram._chatpermissions import ChatPermissions
|
||||
from telegram._files.chatphoto import ChatPhoto
|
||||
from telegram._reaction import ReactionType
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
@@ -200,6 +200,9 @@ class ChatFullInfo(_ChatBase):
|
||||
sent or forwarded to the channel chat. The field is available only for channel chats.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
can_send_gift (:obj:`bool`, optional): :obj:`True`, if gifts can be sent to the chat.
|
||||
|
||||
.. versionadded:: 21.11
|
||||
|
||||
Attributes:
|
||||
id (:obj:`int`): Unique identifier for this chat.
|
||||
@@ -354,6 +357,9 @@ class ChatFullInfo(_ChatBase):
|
||||
sent or forwarded to the channel chat. The field is available only for channel chats.
|
||||
|
||||
.. versionadded:: 21.4
|
||||
can_send_gift (:obj:`bool`): Optional. :obj:`True`, if gifts can be sent to the chat.
|
||||
|
||||
.. versionadded:: 21.11
|
||||
|
||||
.. _accent colors: https://core.telegram.org/bots/api#accent-colors
|
||||
.. _topics: https://telegram.org/blog/topics-in-groups-collectible-usernames#topics-in-groups
|
||||
@@ -369,6 +375,7 @@ class ChatFullInfo(_ChatBase):
|
||||
"business_intro",
|
||||
"business_location",
|
||||
"business_opening_hours",
|
||||
"can_send_gift",
|
||||
"can_send_paid_media",
|
||||
"can_set_sticker_set",
|
||||
"custom_emoji_sticker_set_name",
|
||||
@@ -445,6 +452,7 @@ class ChatFullInfo(_ChatBase):
|
||||
linked_chat_id: Optional[int] = None,
|
||||
location: Optional[ChatLocation] = None,
|
||||
can_send_paid_media: Optional[bool] = None,
|
||||
can_send_gift: Optional[bool] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
@@ -510,17 +518,13 @@ class ChatFullInfo(_ChatBase):
|
||||
self.business_location: Optional[BusinessLocation] = business_location
|
||||
self.business_opening_hours: Optional[BusinessOpeningHours] = business_opening_hours
|
||||
self.can_send_paid_media: Optional[bool] = can_send_paid_media
|
||||
self.can_send_gift: Optional[bool] = can_send_gift
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ChatFullInfo"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChatFullInfo":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
|
||||
@@ -528,7 +532,7 @@ class ChatFullInfo(_ChatBase):
|
||||
data.get("emoji_status_expiration_date"), tzinfo=loc_tzinfo
|
||||
)
|
||||
|
||||
data["photo"] = ChatPhoto.de_json(data.get("photo"), bot)
|
||||
data["photo"] = de_json_optional(data.get("photo"), ChatPhoto, bot)
|
||||
|
||||
from telegram import ( # pylint: disable=import-outside-toplevel
|
||||
BusinessIntro,
|
||||
@@ -537,16 +541,20 @@ class ChatFullInfo(_ChatBase):
|
||||
Message,
|
||||
)
|
||||
|
||||
data["pinned_message"] = Message.de_json(data.get("pinned_message"), bot)
|
||||
data["permissions"] = ChatPermissions.de_json(data.get("permissions"), bot)
|
||||
data["location"] = ChatLocation.de_json(data.get("location"), bot)
|
||||
data["available_reactions"] = ReactionType.de_list(data.get("available_reactions"), bot)
|
||||
data["birthdate"] = Birthdate.de_json(data.get("birthdate"), bot)
|
||||
data["personal_chat"] = Chat.de_json(data.get("personal_chat"), bot)
|
||||
data["business_intro"] = BusinessIntro.de_json(data.get("business_intro"), bot)
|
||||
data["business_location"] = BusinessLocation.de_json(data.get("business_location"), bot)
|
||||
data["business_opening_hours"] = BusinessOpeningHours.de_json(
|
||||
data.get("business_opening_hours"), bot
|
||||
data["pinned_message"] = de_json_optional(data.get("pinned_message"), Message, bot)
|
||||
data["permissions"] = de_json_optional(data.get("permissions"), ChatPermissions, bot)
|
||||
data["location"] = de_json_optional(data.get("location"), ChatLocation, bot)
|
||||
data["available_reactions"] = de_list_optional(
|
||||
data.get("available_reactions"), ReactionType, bot
|
||||
)
|
||||
data["birthdate"] = de_json_optional(data.get("birthdate"), Birthdate, bot)
|
||||
data["personal_chat"] = de_json_optional(data.get("personal_chat"), Chat, bot)
|
||||
data["business_intro"] = de_json_optional(data.get("business_intro"), BusinessIntro, bot)
|
||||
data["business_location"] = de_json_optional(
|
||||
data.get("business_location"), BusinessLocation, bot
|
||||
)
|
||||
data["business_opening_hours"] = de_json_optional(
|
||||
data.get("business_opening_hours"), BusinessOpeningHours, bot
|
||||
)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -22,6 +22,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
@@ -177,19 +178,14 @@ class ChatInviteLink(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ChatInviteLink"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChatInviteLink":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
|
||||
data["creator"] = User.de_json(data.get("creator"), bot)
|
||||
data["creator"] = de_json_optional(data.get("creator"), User, bot)
|
||||
data["expire_date"] = from_timestamp(data.get("expire_date", None), tzinfo=loc_tzinfo)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -24,6 +24,7 @@ from telegram._chat import Chat
|
||||
from telegram._chatinvitelink import ChatInviteLink
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
@@ -129,22 +130,17 @@ class ChatJoinRequest(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ChatJoinRequest"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChatJoinRequest":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
|
||||
data["chat"] = Chat.de_json(data.get("chat"), bot)
|
||||
data["from_user"] = User.de_json(data.pop("from", None), bot)
|
||||
data["chat"] = de_json_optional(data.get("chat"), Chat, bot)
|
||||
data["from_user"] = de_json_optional(data.pop("from", None), User, bot)
|
||||
data["date"] = from_timestamp(data.get("date", None), tzinfo=loc_tzinfo)
|
||||
data["invite_link"] = ChatInviteLink.de_json(data.get("invite_link"), bot)
|
||||
data["invite_link"] = de_json_optional(data.get("invite_link"), ChatInviteLink, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ from typing import TYPE_CHECKING, Final, Optional
|
||||
from telegram import constants
|
||||
from telegram._files.location import Location
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -68,16 +69,11 @@ class ChatLocation(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ChatLocation"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChatLocation":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["location"] = Location.de_json(data.get("location"), bot)
|
||||
data["location"] = de_json_optional(data.get("location"), Location, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ from typing import TYPE_CHECKING, Final, Optional
|
||||
from telegram import constants
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils import enum
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
@@ -98,22 +100,17 @@ class ChatMember(TelegramObject):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
# Required by all subclasses
|
||||
self.user: User = user
|
||||
self.status: str = status
|
||||
self.status: str = enum.get_member(constants.ChatMemberStatus, status, status)
|
||||
|
||||
self._id_attrs = (self.user, self.status)
|
||||
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ChatMember"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChatMember":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[ChatMember]] = {
|
||||
cls.OWNER: ChatMemberOwner,
|
||||
cls.ADMINISTRATOR: ChatMemberAdministrator,
|
||||
@@ -126,12 +123,12 @@ class ChatMember(TelegramObject):
|
||||
if cls is ChatMember and data.get("status") in _class_mapping:
|
||||
return _class_mapping[data.pop("status")].de_json(data=data, bot=bot)
|
||||
|
||||
data["user"] = User.de_json(data.get("user"), bot)
|
||||
data["user"] = de_json_optional(data.get("user"), User, bot)
|
||||
if "until_date" in data:
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
|
||||
data["until_date"] = from_timestamp(data["until_date"], tzinfo=loc_tzinfo)
|
||||
data["until_date"] = from_timestamp(data.get("until_date"), tzinfo=loc_tzinfo)
|
||||
|
||||
# This is a deprecated field that TG still returns for backwards compatibility
|
||||
# Let's filter it out to speed up the de-json process
|
||||
|
||||
@@ -25,6 +25,7 @@ from telegram._chatinvitelink import ChatInviteLink
|
||||
from telegram._chatmember import ChatMember
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
@@ -141,24 +142,19 @@ class ChatMemberUpdated(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ChatMemberUpdated"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChatMemberUpdated":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
|
||||
data["chat"] = Chat.de_json(data.get("chat"), bot)
|
||||
data["from_user"] = User.de_json(data.pop("from", None), bot)
|
||||
data["chat"] = de_json_optional(data.get("chat"), Chat, bot)
|
||||
data["from_user"] = de_json_optional(data.pop("from", None), User, bot)
|
||||
data["date"] = from_timestamp(data.get("date"), tzinfo=loc_tzinfo)
|
||||
data["old_chat_member"] = ChatMember.de_json(data.get("old_chat_member"), bot)
|
||||
data["new_chat_member"] = ChatMember.de_json(data.get("new_chat_member"), bot)
|
||||
data["invite_link"] = ChatInviteLink.de_json(data.get("invite_link"), bot)
|
||||
data["old_chat_member"] = de_json_optional(data.get("old_chat_member"), ChatMember, bot)
|
||||
data["new_chat_member"] = de_json_optional(data.get("new_chat_member"), ChatMember, bot)
|
||||
data["invite_link"] = de_json_optional(data.get("invite_link"), ChatInviteLink, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -231,15 +231,10 @@ class ChatPermissions(TelegramObject):
|
||||
return cls(*(14 * (False,)))
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ChatPermissions"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChatPermissions":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
api_kwargs = {}
|
||||
# This is a deprecated field that TG still returns for backwards compatibility
|
||||
# Let's filter it out to speed up the de-json process
|
||||
|
||||
@@ -24,6 +24,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
from telegram._files.location import Location
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -92,18 +93,13 @@ class ChosenInlineResult(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ChosenInlineResult"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChosenInlineResult":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
# Required
|
||||
data["from_user"] = User.de_json(data.pop("from", None), bot)
|
||||
data["from_user"] = de_json_optional(data.pop("from", None), User, bot)
|
||||
# Optionals
|
||||
data["location"] = Location.de_json(data.get("location"), bot)
|
||||
data["location"] = de_json_optional(data.get("location"), Location, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -21,6 +21,7 @@ from typing import TYPE_CHECKING, Optional, TypeVar
|
||||
|
||||
from telegram._files._basemedium import _BaseMedium
|
||||
from telegram._files.photosize import PhotoSize
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -82,17 +83,14 @@ class _BaseThumbedMedium(_BaseMedium):
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls: type[ThumbedMT_co], data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional[ThumbedMT_co]:
|
||||
cls: type[ThumbedMT_co], data: JSONDict, bot: Optional["Bot"] = None
|
||||
) -> ThumbedMT_co:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
# In case this wasn't already done by the subclass
|
||||
if not isinstance(data.get("thumbnail"), PhotoSize):
|
||||
data["thumbnail"] = PhotoSize.de_json(data.get("thumbnail"), bot)
|
||||
data["thumbnail"] = de_json_optional(data.get("thumbnail"), PhotoSize, bot)
|
||||
|
||||
api_kwargs = {}
|
||||
# This is a deprecated field that TG still returns for backwards compatibility
|
||||
|
||||
@@ -214,6 +214,13 @@ class InputPaidMediaVideo(InputPaidMedia):
|
||||
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|
|
||||
cover (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||
optional): Cover for the video in the message. |fileinputnopath|
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
start_timestamp (:obj:`int`, optional): Start timestamp for the video in the message
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
width (:obj:`int`, optional): Video width.
|
||||
height (:obj:`int`, optional): Video height.
|
||||
duration (:obj:`int`, optional): Video duration in seconds.
|
||||
@@ -225,6 +232,13 @@ class InputPaidMediaVideo(InputPaidMedia):
|
||||
:tg-const:`telegram.constants.InputPaidMediaType.VIDEO`.
|
||||
media (:obj:`str` | :class:`telegram.InputFile`): Video to send.
|
||||
thumbnail (:class:`telegram.InputFile`): Optional. |thumbdocstringbase|
|
||||
cover (:class:`telegram.InputFile`): Optional. Cover for the video in the message.
|
||||
|fileinputnopath|
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
start_timestamp (:obj:`int`): Optional. Start timestamp for the video in the message
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
width (:obj:`int`): Optional. Video width.
|
||||
height (:obj:`int`): Optional. Video height.
|
||||
duration (:obj:`int`): Optional. Video duration in seconds.
|
||||
@@ -232,7 +246,15 @@ class InputPaidMediaVideo(InputPaidMedia):
|
||||
suitable for streaming.
|
||||
"""
|
||||
|
||||
__slots__ = ("duration", "height", "supports_streaming", "thumbnail", "width")
|
||||
__slots__ = (
|
||||
"cover",
|
||||
"duration",
|
||||
"height",
|
||||
"start_timestamp",
|
||||
"supports_streaming",
|
||||
"thumbnail",
|
||||
"width",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -242,6 +264,8 @@ class InputPaidMediaVideo(InputPaidMedia):
|
||||
height: Optional[int] = None,
|
||||
duration: Optional[int] = None,
|
||||
supports_streaming: Optional[bool] = None,
|
||||
cover: Optional[FileInput] = None,
|
||||
start_timestamp: Optional[int] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
@@ -264,6 +288,10 @@ class InputPaidMediaVideo(InputPaidMedia):
|
||||
self.height: Optional[int] = height
|
||||
self.duration: Optional[int] = duration
|
||||
self.supports_streaming: Optional[bool] = supports_streaming
|
||||
self.cover: Optional[Union[InputFile, str]] = (
|
||||
parse_file_input(cover, attach=True, local_mode=True) if cover else None
|
||||
)
|
||||
self.start_timestamp: Optional[int] = start_timestamp
|
||||
|
||||
|
||||
class InputMediaAnimation(InputMedia):
|
||||
@@ -536,6 +564,13 @@ class InputMediaVideo(InputMedia):
|
||||
optional): |thumbdocstringnopath|
|
||||
|
||||
.. versionadded:: 20.2
|
||||
cover (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \
|
||||
optional): Cover for the video in the message. |fileinputnopath|
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
start_timestamp (:obj:`int`, optional): Start timestamp for the video in the message
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med|
|
||||
|
||||
.. versionadded:: 21.3
|
||||
@@ -568,13 +603,22 @@ class InputMediaVideo(InputMedia):
|
||||
show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med|
|
||||
|
||||
.. versionadded:: 21.3
|
||||
cover (:class:`telegram.InputFile`): Optional. Cover for the video in the message.
|
||||
|fileinputnopath|
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
start_timestamp (:obj:`int`): Optional. Start timestamp for the video in the message
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"cover",
|
||||
"duration",
|
||||
"has_spoiler",
|
||||
"height",
|
||||
"show_caption_above_media",
|
||||
"start_timestamp",
|
||||
"supports_streaming",
|
||||
"thumbnail",
|
||||
"width",
|
||||
@@ -594,6 +638,8 @@ class InputMediaVideo(InputMedia):
|
||||
has_spoiler: Optional[bool] = None,
|
||||
thumbnail: Optional[FileInput] = None,
|
||||
show_caption_above_media: Optional[bool] = None,
|
||||
cover: Optional[FileInput] = None,
|
||||
start_timestamp: Optional[int] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
@@ -625,6 +671,10 @@ class InputMediaVideo(InputMedia):
|
||||
self.supports_streaming: Optional[bool] = supports_streaming
|
||||
self.has_spoiler: Optional[bool] = has_spoiler
|
||||
self.show_caption_above_media: Optional[bool] = show_caption_above_media
|
||||
self.cover: Optional[Union[InputFile, str]] = (
|
||||
parse_file_input(cover, attach=True, local_mode=True) if cover else None
|
||||
)
|
||||
self.start_timestamp: Optional[int] = start_timestamp
|
||||
|
||||
|
||||
class InputMediaAudio(InputMedia):
|
||||
|
||||
@@ -61,8 +61,8 @@ class InputSticker(TelegramObject):
|
||||
format (:obj:`str`): Format of the added sticker, must be one of
|
||||
:tg-const:`telegram.constants.StickerFormat.STATIC` for a
|
||||
``.WEBP`` or ``.PNG`` image, :tg-const:`telegram.constants.StickerFormat.ANIMATED`
|
||||
for a ``.TGS`` animation, :tg-const:`telegram.constants.StickerFormat.VIDEO` for a WEBM
|
||||
video.
|
||||
for a ``.TGS`` animation, :tg-const:`telegram.constants.StickerFormat.VIDEO` for a
|
||||
``.WEBM`` video.
|
||||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
@@ -84,8 +84,8 @@ class InputSticker(TelegramObject):
|
||||
format (:obj:`str`): Format of the added sticker, must be one of
|
||||
:tg-const:`telegram.constants.StickerFormat.STATIC` for a
|
||||
``.WEBP`` or ``.PNG`` image, :tg-const:`telegram.constants.StickerFormat.ANIMATED`
|
||||
for a ``.TGS`` animation, :tg-const:`telegram.constants.StickerFormat.VIDEO` for a WEBM
|
||||
video.
|
||||
for a ``.TGS`` animation, :tg-const:`telegram.constants.StickerFormat.VIDEO` for a
|
||||
``.WEBM`` video.
|
||||
|
||||
.. versionadded:: 21.1
|
||||
"""
|
||||
|
||||
@@ -26,7 +26,7 @@ from telegram._files.file import File
|
||||
from telegram._files.photosize import PhotoSize
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils import enum
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -194,16 +194,13 @@ class Sticker(_BaseThumbedMedium):
|
||||
""":const:`telegram.constants.StickerType.CUSTOM_EMOJI`"""
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["Sticker"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Sticker":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["thumbnail"] = PhotoSize.de_json(data.get("thumbnail"), bot)
|
||||
data["mask_position"] = MaskPosition.de_json(data.get("mask_position"), bot)
|
||||
data["premium_animation"] = File.de_json(data.get("premium_animation"), bot)
|
||||
data["thumbnail"] = de_json_optional(data.get("thumbnail"), PhotoSize, bot)
|
||||
data["mask_position"] = de_json_optional(data.get("mask_position"), MaskPosition, bot)
|
||||
data["premium_animation"] = de_json_optional(data.get("premium_animation"), File, bot)
|
||||
|
||||
api_kwargs = {}
|
||||
# This is a deprecated field that TG still returns for backwards compatibility
|
||||
@@ -306,15 +303,12 @@ class StickerSet(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["StickerSet"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "StickerSet":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
if not data:
|
||||
return None
|
||||
data = cls._parse_data(data)
|
||||
|
||||
data["thumbnail"] = PhotoSize.de_json(data.get("thumbnail"), bot)
|
||||
data["stickers"] = Sticker.de_list(data.get("stickers"), bot)
|
||||
data["thumbnail"] = de_json_optional(data.get("thumbnail"), PhotoSize, bot)
|
||||
data["stickers"] = de_list_optional(data.get("stickers"), Sticker, bot)
|
||||
|
||||
api_kwargs = {}
|
||||
# These are deprecated fields that TG still returns for backwards compatibility
|
||||
|
||||
@@ -22,6 +22,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._files.location import Location
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -103,13 +104,10 @@ class Venue(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["Venue"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Venue":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["location"] = Location.de_json(data.get("location"), bot)
|
||||
data["location"] = de_json_optional(data.get("location"), Location, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -17,12 +17,17 @@
|
||||
# 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 Video."""
|
||||
from typing import Optional
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._files._basethumbedmedium import _BaseThumbedMedium
|
||||
from telegram._files.photosize import PhotoSize
|
||||
from telegram._utils.argumentparsing import de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class Video(_BaseThumbedMedium):
|
||||
"""This object represents a video file.
|
||||
@@ -48,6 +53,13 @@ class Video(_BaseThumbedMedium):
|
||||
thumbnail (:class:`telegram.PhotoSize`, optional): Video thumbnail.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
cover (Sequence[:class:`telegram.PhotoSize`], optional): Available sizes of the cover of
|
||||
the video in the message.
|
||||
|
||||
.. versionadded:: 21.11
|
||||
start_timestamp (:obj:`int`, optional): Timestamp in seconds from which the video
|
||||
will play in the message
|
||||
.. versionadded:: 21.11
|
||||
|
||||
Attributes:
|
||||
file_id (:obj:`str`): Identifier for this file, which can be used to download
|
||||
@@ -64,9 +76,24 @@ class Video(_BaseThumbedMedium):
|
||||
thumbnail (:class:`telegram.PhotoSize`): Optional. Video thumbnail.
|
||||
|
||||
.. versionadded:: 20.2
|
||||
cover (tuple[:class:`telegram.PhotoSize`]): Optional, Available sizes of the cover of
|
||||
the video in the message.
|
||||
|
||||
.. versionadded:: 21.11
|
||||
start_timestamp (:obj:`int`): Optional, Timestamp in seconds from which the video
|
||||
will play in the message
|
||||
.. versionadded:: 21.11
|
||||
"""
|
||||
|
||||
__slots__ = ("duration", "file_name", "height", "mime_type", "width")
|
||||
__slots__ = (
|
||||
"cover",
|
||||
"duration",
|
||||
"file_name",
|
||||
"height",
|
||||
"mime_type",
|
||||
"start_timestamp",
|
||||
"width",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -79,6 +106,8 @@ class Video(_BaseThumbedMedium):
|
||||
file_size: Optional[int] = None,
|
||||
file_name: Optional[str] = None,
|
||||
thumbnail: Optional[PhotoSize] = None,
|
||||
cover: Optional[Sequence[PhotoSize]] = None,
|
||||
start_timestamp: Optional[int] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
@@ -97,3 +126,14 @@ class Video(_BaseThumbedMedium):
|
||||
# Optional
|
||||
self.mime_type: Optional[str] = mime_type
|
||||
self.file_name: Optional[str] = file_name
|
||||
self.cover: Optional[Sequence[PhotoSize]] = parse_sequence_arg(cover)
|
||||
self.start_timestamp: Optional[int] = start_timestamp
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Video":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
data["cover"] = de_list_optional(data.get("cover"), PhotoSize, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -24,7 +24,7 @@ from telegram._files.animation import Animation
|
||||
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.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.strings import TextEncoding
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
@@ -124,16 +124,13 @@ class Game(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["Game"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Game":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["photo"] = PhotoSize.de_list(data.get("photo"), bot)
|
||||
data["text_entities"] = MessageEntity.de_list(data.get("text_entities"), bot)
|
||||
data["animation"] = Animation.de_json(data.get("animation"), bot)
|
||||
data["photo"] = de_list_optional(data.get("photo"), PhotoSize, bot)
|
||||
data["text_entities"] = de_list_optional(data.get("text_entities"), MessageEntity, bot)
|
||||
data["animation"] = de_json_optional(data.get("animation"), Animation, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -61,15 +62,10 @@ class GameHighScore(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["GameHighScore"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "GameHighScore":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["user"] = User.de_json(data.get("user"), bot)
|
||||
data["user"] = de_json_optional(data.get("user"), User, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
+5
-11
@@ -23,7 +23,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._files.sticker import Sticker
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -99,14 +99,11 @@ class Gift(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["Gift"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Gift":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["sticker"] = Sticker.de_json(data.get("sticker"), bot)
|
||||
data["sticker"] = de_json_optional(data.get("sticker"), Sticker, bot)
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -142,12 +139,9 @@ class Gifts(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["Gifts"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Gifts":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["gifts"] = Gift.de_list(data.get("gifts"), bot)
|
||||
data["gifts"] = de_list_optional(data.get("gifts"), Gift, bot)
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
+8
-23
@@ -24,7 +24,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
from telegram._chat import Chat
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
@@ -137,19 +137,14 @@ class Giveaway(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["Giveaway"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Giveaway":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
|
||||
data["chats"] = tuple(Chat.de_list(data.get("chats"), bot))
|
||||
data["chats"] = de_list_optional(data.get("chats"), Chat, bot)
|
||||
data["winners_selection_date"] = from_timestamp(
|
||||
data.get("winners_selection_date"), tzinfo=loc_tzinfo
|
||||
)
|
||||
@@ -299,20 +294,15 @@ class GiveawayWinners(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["GiveawayWinners"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "GiveawayWinners":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
|
||||
data["chat"] = Chat.de_json(data.get("chat"), bot)
|
||||
data["winners"] = tuple(User.de_list(data.get("winners"), bot))
|
||||
data["chat"] = de_json_optional(data.get("chat"), Chat, bot)
|
||||
data["winners"] = de_list_optional(data.get("winners"), User, bot)
|
||||
data["winners_selection_date"] = from_timestamp(
|
||||
data.get("winners_selection_date"), tzinfo=loc_tzinfo
|
||||
)
|
||||
@@ -376,18 +366,13 @@ class GiveawayCompleted(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["GiveawayCompleted"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "GiveawayCompleted":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
# Unfortunately, this needs to be here due to cyclic imports
|
||||
from telegram._message import Message # pylint: disable=import-outside-toplevel
|
||||
|
||||
data["giveaway_message"] = Message.de_json(data.get("giveaway_message"), bot)
|
||||
data["giveaway_message"] = de_json_optional(data.get("giveaway_message"), Message, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -26,6 +26,7 @@ from telegram._games.callbackgame import CallbackGame
|
||||
from telegram._loginurl import LoginUrl
|
||||
from telegram._switchinlinequerychosenchat import SwitchInlineQueryChosenChat
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._webappinfo import WebAppInfo
|
||||
|
||||
@@ -296,22 +297,17 @@ class InlineKeyboardButton(TelegramObject):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["InlineKeyboardButton"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "InlineKeyboardButton":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["login_url"] = LoginUrl.de_json(data.get("login_url"), bot)
|
||||
data["web_app"] = WebAppInfo.de_json(data.get("web_app"), bot)
|
||||
data["callback_game"] = CallbackGame.de_json(data.get("callback_game"), bot)
|
||||
data["switch_inline_query_chosen_chat"] = SwitchInlineQueryChosenChat.de_json(
|
||||
data.get("switch_inline_query_chosen_chat"), bot
|
||||
data["login_url"] = de_json_optional(data.get("login_url"), LoginUrl, bot)
|
||||
data["web_app"] = de_json_optional(data.get("web_app"), WebAppInfo, bot)
|
||||
data["callback_game"] = de_json_optional(data.get("callback_game"), CallbackGame, bot)
|
||||
data["switch_inline_query_chosen_chat"] = de_json_optional(
|
||||
data.get("switch_inline_query_chosen_chat"), SwitchInlineQueryChosenChat, bot
|
||||
)
|
||||
data["copy_text"] = CopyTextButton.de_json(data.get("copy_text"), bot)
|
||||
data["copy_text"] = de_json_optional(data.get("copy_text"), CopyTextButton, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -91,12 +91,8 @@ class InlineKeyboardMarkup(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["InlineKeyboardMarkup"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "InlineKeyboardMarkup":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
if not data:
|
||||
return None
|
||||
|
||||
keyboard = []
|
||||
for row in data["inline_keyboard"]:
|
||||
|
||||
@@ -27,8 +27,9 @@ from telegram._files.location import Location
|
||||
from telegram._inline.inlinequeryresultsbutton import InlineQueryResultsButton
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
from telegram._utils.types import JSONDict, ODVInput, TimePeriod
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot, InlineQueryResult
|
||||
@@ -61,6 +62,12 @@ class InlineQuery(TelegramObject):
|
||||
``auto_pagination``. Use a named argument for those,
|
||||
and notice that some positional arguments changed position as a result.
|
||||
|
||||
.. versionchanged:: 22.0
|
||||
Removed constants ``MIN_START_PARAMETER_LENGTH`` and ``MAX_START_PARAMETER_LENGTH``.
|
||||
Use :attr:`telegram.constants.InlineQueryResultsButtonLimit.MIN_START_PARAMETER_LENGTH` and
|
||||
:attr:`telegram.constants.InlineQueryResultsButtonLimit.MAX_START_PARAMETER_LENGTH`
|
||||
instead.
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier for this query.
|
||||
from_user (:class:`telegram.User`): Sender.
|
||||
@@ -126,17 +133,12 @@ class InlineQuery(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["InlineQuery"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "InlineQuery":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["from_user"] = User.de_json(data.pop("from", None), bot)
|
||||
data["location"] = Location.de_json(data.get("location"), bot)
|
||||
data["from_user"] = de_json_optional(data.pop("from", None), User, bot)
|
||||
data["location"] = de_json_optional(data.get("location"), Location, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -145,7 +147,7 @@ class InlineQuery(TelegramObject):
|
||||
results: Union[
|
||||
Sequence["InlineQueryResult"], Callable[[int], Optional[Sequence["InlineQueryResult"]]]
|
||||
],
|
||||
cache_time: Optional[int] = None,
|
||||
cache_time: Optional[TimePeriod] = None,
|
||||
is_personal: Optional[bool] = None,
|
||||
next_offset: Optional[str] = None,
|
||||
button: Optional[InlineQueryResultsButton] = None,
|
||||
@@ -206,16 +208,6 @@ class InlineQuery(TelegramObject):
|
||||
|
||||
.. versionadded:: 13.2
|
||||
"""
|
||||
MIN_SWITCH_PM_TEXT_LENGTH: Final[int] = constants.InlineQueryLimit.MIN_SWITCH_PM_TEXT_LENGTH
|
||||
""":const:`telegram.constants.InlineQueryLimit.MIN_SWITCH_PM_TEXT_LENGTH`
|
||||
|
||||
.. versionadded:: 20.0
|
||||
"""
|
||||
MAX_SWITCH_PM_TEXT_LENGTH: Final[int] = constants.InlineQueryLimit.MAX_SWITCH_PM_TEXT_LENGTH
|
||||
""":const:`telegram.constants.InlineQueryLimit.MAX_SWITCH_PM_TEXT_LENGTH`
|
||||
|
||||
.. versionadded:: 20.0
|
||||
"""
|
||||
MAX_OFFSET_LENGTH: Final[int] = constants.InlineQueryLimit.MAX_OFFSET_LENGTH
|
||||
""":const:`telegram.constants.InlineQueryLimit.MAX_OFFSET_LENGTH`
|
||||
|
||||
|
||||
@@ -23,9 +23,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup
|
||||
from telegram._inline.inlinequeryresult import InlineQueryResult
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.constants import InlineQueryResultType
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import InputMessageContent
|
||||
@@ -40,6 +38,9 @@ class InlineQueryResultArticle(InlineQueryResult):
|
||||
.. versionchanged:: 20.5
|
||||
Removed the deprecated arguments and attributes ``thumb_*``.
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
Removed the deprecated argument and attribute ``hide_url``.
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier for this result,
|
||||
:tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`-
|
||||
@@ -50,12 +51,9 @@ class InlineQueryResultArticle(InlineQueryResult):
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message.
|
||||
url (:obj:`str`, optional): URL of the result.
|
||||
hide_url (:obj:`bool`, optional): Pass :obj:`True`, if you don't want the URL to be shown
|
||||
in the message.
|
||||
|
||||
.. deprecated:: 21.10
|
||||
This attribute will be removed in future PTB versions. Pass an empty string as URL
|
||||
instead.
|
||||
Tip:
|
||||
Pass an empty string as URL if you don't want the URL to be shown in the message.
|
||||
description (:obj:`str`, optional): Short description of the result.
|
||||
thumbnail_url (:obj:`str`, optional): Url of the thumbnail for the result.
|
||||
|
||||
@@ -78,12 +76,6 @@ class InlineQueryResultArticle(InlineQueryResult):
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
to the message.
|
||||
url (:obj:`str`): Optional. URL of the result.
|
||||
hide_url (:obj:`bool`): Optional. Pass :obj:`True`, if you don't want the URL to be shown
|
||||
in the message.
|
||||
|
||||
.. deprecated:: 21.10
|
||||
This attribute will be removed in future PTB versions. Pass an empty string as URL
|
||||
instead.
|
||||
description (:obj:`str`): Optional. Short description of the result.
|
||||
thumbnail_url (:obj:`str`): Optional. Url of the thumbnail for the result.
|
||||
|
||||
@@ -99,7 +91,6 @@ class InlineQueryResultArticle(InlineQueryResult):
|
||||
|
||||
__slots__ = (
|
||||
"description",
|
||||
"hide_url",
|
||||
"input_message_content",
|
||||
"reply_markup",
|
||||
"thumbnail_height",
|
||||
@@ -116,7 +107,6 @@ class InlineQueryResultArticle(InlineQueryResult):
|
||||
input_message_content: "InputMessageContent",
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None,
|
||||
url: Optional[str] = None,
|
||||
hide_url: Optional[bool] = None,
|
||||
description: Optional[str] = None,
|
||||
thumbnail_url: Optional[str] = None,
|
||||
thumbnail_width: Optional[int] = None,
|
||||
@@ -133,16 +123,6 @@ class InlineQueryResultArticle(InlineQueryResult):
|
||||
# Optional
|
||||
self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup
|
||||
self.url: Optional[str] = url
|
||||
if hide_url is not None:
|
||||
warn(
|
||||
PTBDeprecationWarning(
|
||||
"21.10",
|
||||
"The argument `hide_url` will be removed in future PTB"
|
||||
"versions. Pass an empty string as URL instead.",
|
||||
),
|
||||
stacklevel=2,
|
||||
)
|
||||
self.hide_url: Optional[bool] = hide_url
|
||||
self.description: Optional[str] = description
|
||||
self.thumbnail_url: Optional[str] = thumbnail_url
|
||||
self.thumbnail_width: Optional[int] = thumbnail_width
|
||||
|
||||
@@ -47,7 +47,7 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result,
|
||||
:tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`-
|
||||
:tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes.
|
||||
gif_url (:obj:`str`): A valid URL for the GIF file. File size must not exceed 1MB.
|
||||
gif_url (:obj:`str`): A valid URL for the GIF file.
|
||||
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.
|
||||
@@ -86,7 +86,7 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result,
|
||||
:tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`-
|
||||
:tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes.
|
||||
gif_url (:obj:`str`): A valid URL for the GIF file. File size must not exceed 1MB.
|
||||
gif_url (:obj:`str`): A valid URL for the GIF file.
|
||||
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.
|
||||
|
||||
@@ -48,7 +48,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result,
|
||||
:tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`-
|
||||
:tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes.
|
||||
mpeg4_url (:obj:`str`): A valid URL for the MP4 file. File size must not exceed 1MB.
|
||||
mpeg4_url (:obj:`str`): A valid URL for the MP4 file.
|
||||
mpeg4_width (:obj:`int`, optional): Video width.
|
||||
mpeg4_height (:obj:`int`, optional): Video height.
|
||||
mpeg4_duration (:obj:`int`, optional): Video duration in seconds.
|
||||
@@ -88,7 +88,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result,
|
||||
:tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`-
|
||||
:tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes.
|
||||
mpeg4_url (:obj:`str`): A valid URL for the MP4 file. File size must not exceed 1MB.
|
||||
mpeg4_url (:obj:`str`): A valid URL for the MP4 file.
|
||||
mpeg4_width (:obj:`int`): Optional. Video width.
|
||||
mpeg4_height (:obj:`int`): Optional. Video height.
|
||||
mpeg4_duration (:obj:`int`): Optional. Video duration in seconds.
|
||||
|
||||
@@ -22,6 +22,7 @@ from typing import TYPE_CHECKING, Final, Optional
|
||||
|
||||
from telegram import constants
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._webappinfo import WebAppInfo
|
||||
|
||||
@@ -46,9 +47,10 @@ class InlineQueryResultsButton(TelegramObject):
|
||||
inside the Web App.
|
||||
start_parameter (:obj:`str`, optional): Deep-linking parameter for the
|
||||
:guilabel:`/start` message sent to the bot when user presses the switch button.
|
||||
:tg-const:`telegram.InlineQuery.MIN_SWITCH_PM_TEXT_LENGTH`-
|
||||
:tg-const:`telegram.InlineQuery.MAX_SWITCH_PM_TEXT_LENGTH` characters,
|
||||
only ``A-Z``, ``a-z``, ``0-9``, ``_`` and ``-`` are allowed.
|
||||
:tg-const:`telegram.constants.InlineQueryResultsButtonLimit.MIN_START_PARAMETER_LENGTH`
|
||||
-
|
||||
:tg-const:`telegram.constants.InlineQueryResultsButtonLimit.MAX_START_PARAMETER_LENGTH`
|
||||
characters, only ``A-Z``, ``a-z``, ``0-9``, ``_`` and ``-`` are allowed.
|
||||
|
||||
Example:
|
||||
An inline bot that sends YouTube videos can ask the user to connect the bot to
|
||||
@@ -66,10 +68,10 @@ class InlineQueryResultsButton(TelegramObject):
|
||||
user presses the button. The Web App will be able to switch back to the inline mode
|
||||
using the method ``web_app_switch_inline_query`` inside the Web App.
|
||||
start_parameter (:obj:`str`): Optional. Deep-linking parameter for the
|
||||
:guilabel:`/start` message sent to the bot when user presses the switch button.
|
||||
:tg-const:`telegram.InlineQuery.MIN_SWITCH_PM_TEXT_LENGTH`-
|
||||
:tg-const:`telegram.InlineQuery.MAX_SWITCH_PM_TEXT_LENGTH` characters,
|
||||
only ``A-Z``, ``a-z``, ``0-9``, ``_`` and ``-`` are allowed.
|
||||
:tg-const:`telegram.constants.InlineQueryResultsButtonLimit.MIN_START_PARAMETER_LENGTH`
|
||||
-
|
||||
:tg-const:`telegram.constants.InlineQueryResultsButtonLimit.MAX_START_PARAMETER_LENGTH`
|
||||
characters, only ``A-Z``, ``a-z``, ``0-9``, ``_`` and ``-`` are allowed.
|
||||
|
||||
"""
|
||||
|
||||
@@ -97,14 +99,10 @@ class InlineQueryResultsButton(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["InlineQueryResultsButton"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "InlineQueryResultsButton":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["web_app"] = WebAppInfo.de_json(data.get("web_app"), bot)
|
||||
data["web_app"] = de_json_optional(data.get("web_app"), WebAppInfo, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._inline.inputmessagecontent import InputMessageContent
|
||||
from telegram._payment.labeledprice import LabeledPrice
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.argumentparsing import de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -35,9 +35,11 @@ class InputInvoiceMessageContent(InputMessageContent):
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`title`, :attr:`description`, :attr:`payload`,
|
||||
:attr:`provider_token`, :attr:`currency` and :attr:`prices` are equal.
|
||||
:attr:`currency` and :attr:`prices` are equal.
|
||||
|
||||
.. versionadded:: 13.5
|
||||
.. versionchanged:: 21.11
|
||||
:attr:`provider_token` is no longer considered for equality comparison.
|
||||
|
||||
Args:
|
||||
title (:obj:`str`): Product name. :tg-const:`telegram.Invoice.MIN_TITLE_LENGTH`-
|
||||
@@ -49,13 +51,13 @@ class InputInvoiceMessageContent(InputMessageContent):
|
||||
:tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`-
|
||||
:tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be displayed
|
||||
to the user, use it for your internal processes.
|
||||
provider_token (:obj:`str`): Payment provider token, obtained via
|
||||
provider_token (:obj:`str`, optional): Payment provider token, obtained via
|
||||
`@Botfather <https://t.me/Botfather>`_. Pass an empty string for payments in
|
||||
|tg_stars|.
|
||||
|
||||
.. deprecated:: 21.3
|
||||
As of Bot API 7.4, this parameter is now optional and future versions of the
|
||||
library will make it optional as well.
|
||||
.. versionchanged:: 21.11
|
||||
Bot API 7.4 made this parameter is optional and this is now reflected in the
|
||||
class signature.
|
||||
currency (:obj:`str`): Three-letter ISO 4217 currency code, see more on
|
||||
`currencies <https://core.telegram.org/bots/payments#supported-currencies>`_.
|
||||
Pass ``XTR`` for payments in |tg_stars|.
|
||||
@@ -199,9 +201,9 @@ class InputInvoiceMessageContent(InputMessageContent):
|
||||
title: str,
|
||||
description: str,
|
||||
payload: str,
|
||||
provider_token: Optional[str], # This arg is now optional since Bot API 7.4
|
||||
currency: str,
|
||||
prices: Sequence[LabeledPrice],
|
||||
provider_token: Optional[str] = None,
|
||||
max_tip_amount: Optional[int] = None,
|
||||
suggested_tip_amounts: Optional[Sequence[int]] = None,
|
||||
provider_data: Optional[str] = None,
|
||||
@@ -225,10 +227,10 @@ class InputInvoiceMessageContent(InputMessageContent):
|
||||
self.title: str = title
|
||||
self.description: str = description
|
||||
self.payload: str = payload
|
||||
self.provider_token: Optional[str] = provider_token
|
||||
self.currency: str = currency
|
||||
self.prices: tuple[LabeledPrice, ...] = parse_sequence_arg(prices)
|
||||
# Optionals
|
||||
self.provider_token: Optional[str] = provider_token
|
||||
self.max_tip_amount: Optional[int] = max_tip_amount
|
||||
self.suggested_tip_amounts: tuple[int, ...] = parse_sequence_arg(suggested_tip_amounts)
|
||||
self.provider_data: Optional[str] = provider_data
|
||||
@@ -248,21 +250,15 @@ class InputInvoiceMessageContent(InputMessageContent):
|
||||
self.title,
|
||||
self.description,
|
||||
self.payload,
|
||||
self.provider_token,
|
||||
self.currency,
|
||||
self.prices,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["InputInvoiceMessageContent"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "InputInvoiceMessageContent":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["prices"] = LabeledPrice.de_list(data.get("prices"), bot)
|
||||
data["prices"] = de_list_optional(data.get("prices"), LabeledPrice, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -67,15 +67,10 @@ class PreparedInlineMessage(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PreparedInlineMessage"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "PreparedInlineMessage":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
data["expiration_date"] = from_timestamp(data.get("expiration_date"), tzinfo=loc_tzinfo)
|
||||
|
||||
+12
-10
@@ -23,6 +23,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
from telegram._keyboardbuttonpolltype import KeyboardButtonPollType
|
||||
from telegram._keyboardbuttonrequest import KeyboardButtonRequestChat, KeyboardButtonRequestUsers
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._webappinfo import WebAppInfo
|
||||
|
||||
@@ -168,19 +169,20 @@ class KeyboardButton(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["KeyboardButton"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "KeyboardButton":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["request_poll"] = KeyboardButtonPollType.de_json(data.get("request_poll"), bot)
|
||||
data["request_users"] = KeyboardButtonRequestUsers.de_json(data.get("request_users"), bot)
|
||||
data["request_chat"] = KeyboardButtonRequestChat.de_json(data.get("request_chat"), bot)
|
||||
data["web_app"] = WebAppInfo.de_json(data.get("web_app"), bot)
|
||||
data["request_poll"] = de_json_optional(
|
||||
data.get("request_poll"), KeyboardButtonPollType, bot
|
||||
)
|
||||
data["request_users"] = de_json_optional(
|
||||
data.get("request_users"), KeyboardButtonRequestUsers, bot
|
||||
)
|
||||
data["request_chat"] = de_json_optional(
|
||||
data.get("request_chat"), KeyboardButtonRequestChat, bot
|
||||
)
|
||||
data["web_app"] = de_json_optional(data.get("web_app"), WebAppInfo, bot)
|
||||
|
||||
api_kwargs = {}
|
||||
# This is a deprecated field that TG still returns for backwards compatibility
|
||||
|
||||
@@ -22,6 +22,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._chatadministratorrights import ChatAdministratorRights
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -257,20 +258,15 @@ class KeyboardButtonRequestChat(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["KeyboardButtonRequestChat"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "KeyboardButtonRequestChat":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["user_administrator_rights"] = ChatAdministratorRights.de_json(
|
||||
data.get("user_administrator_rights"), bot
|
||||
data["user_administrator_rights"] = de_json_optional(
|
||||
data.get("user_administrator_rights"), ChatAdministratorRights, bot
|
||||
)
|
||||
data["bot_administrator_rights"] = ChatAdministratorRights.de_json(
|
||||
data.get("bot_administrator_rights"), bot
|
||||
data["bot_administrator_rights"] = de_json_optional(
|
||||
data.get("bot_administrator_rights"), ChatAdministratorRights, bot
|
||||
)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
+4
-16
@@ -22,6 +22,7 @@ from typing import TYPE_CHECKING, Final, Optional
|
||||
from telegram import constants
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils import enum
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._webappinfo import WebAppInfo
|
||||
|
||||
@@ -69,9 +70,7 @@ class MenuButton(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["MenuButton"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "MenuButton":
|
||||
"""Converts JSON data to the appropriate :class:`MenuButton` object, i.e. takes
|
||||
care of selecting the correct subclass.
|
||||
|
||||
@@ -89,12 +88,6 @@ class MenuButton(TelegramObject):
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
if not data and cls is MenuButton:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[MenuButton]] = {
|
||||
cls.COMMANDS: MenuButtonCommands,
|
||||
cls.WEB_APP: MenuButtonWebApp,
|
||||
@@ -172,16 +165,11 @@ class MenuButtonWebApp(MenuButton):
|
||||
self._id_attrs = (self.type, self.text, self.web_app)
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["MenuButtonWebApp"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "MenuButtonWebApp":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["web_app"] = WebAppInfo.de_json(data.get("web_app"), bot)
|
||||
data["web_app"] = de_json_optional(data.get("web_app"), WebAppInfo, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
+217
-244
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,7 @@ from telegram import constants
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils import enum
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.strings import TextEncoding
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
@@ -137,16 +138,11 @@ class MessageEntity(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["MessageEntity"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "MessageEntity":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["user"] = User.de_json(data.get("user"), bot)
|
||||
data["user"] = de_json_optional(data.get("user"), User, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ from telegram._chat import Chat
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils import enum
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
@@ -94,17 +95,12 @@ class MessageOrigin(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["MessageOrigin"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "MessageOrigin":
|
||||
"""Converts JSON data to the appropriate :class:`MessageOrigin` object, i.e. takes
|
||||
care of selecting the correct subclass.
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[MessageOrigin]] = {
|
||||
cls.USER: MessageOriginUser,
|
||||
cls.HIDDEN_USER: MessageOriginHiddenUser,
|
||||
@@ -118,13 +114,13 @@ class MessageOrigin(TelegramObject):
|
||||
data["date"] = from_timestamp(data.get("date"), tzinfo=loc_tzinfo)
|
||||
|
||||
if "sender_user" in data:
|
||||
data["sender_user"] = User.de_json(data.get("sender_user"), bot)
|
||||
data["sender_user"] = de_json_optional(data.get("sender_user"), User, bot)
|
||||
|
||||
if "sender_chat" in data:
|
||||
data["sender_chat"] = Chat.de_json(data.get("sender_chat"), bot)
|
||||
data["sender_chat"] = de_json_optional(data.get("sender_chat"), Chat, bot)
|
||||
|
||||
if "chat" in data:
|
||||
data["chat"] = Chat.de_json(data.get("chat"), bot)
|
||||
data["chat"] = de_json_optional(data.get("chat"), Chat, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ from telegram._chat import Chat
|
||||
from telegram._reaction import ReactionCount, ReactionType
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
@@ -86,21 +86,16 @@ class MessageReactionCountUpdated(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["MessageReactionCountUpdated"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "MessageReactionCountUpdated":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
|
||||
data["date"] = from_timestamp(data.get("date"), tzinfo=loc_tzinfo)
|
||||
data["chat"] = Chat.de_json(data.get("chat"), bot)
|
||||
data["reactions"] = ReactionCount.de_list(data.get("reactions"), bot)
|
||||
data["chat"] = de_json_optional(data.get("chat"), Chat, bot)
|
||||
data["reactions"] = de_list_optional(data.get("reactions"), ReactionCount, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -187,23 +182,18 @@ class MessageReactionUpdated(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["MessageReactionUpdated"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "MessageReactionUpdated":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
|
||||
data["date"] = from_timestamp(data.get("date"), tzinfo=loc_tzinfo)
|
||||
data["chat"] = Chat.de_json(data.get("chat"), bot)
|
||||
data["old_reaction"] = ReactionType.de_list(data.get("old_reaction"), bot)
|
||||
data["new_reaction"] = ReactionType.de_list(data.get("new_reaction"), bot)
|
||||
data["user"] = User.de_json(data.get("user"), bot)
|
||||
data["actor_chat"] = Chat.de_json(data.get("actor_chat"), bot)
|
||||
data["chat"] = de_json_optional(data.get("chat"), Chat, bot)
|
||||
data["old_reaction"] = de_list_optional(data.get("old_reaction"), ReactionType, bot)
|
||||
data["new_reaction"] = de_list_optional(data.get("new_reaction"), ReactionType, bot)
|
||||
data["user"] = de_json_optional(data.get("user"), User, bot)
|
||||
data["actor_chat"] = de_json_optional(data.get("actor_chat"), Chat, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
+9
-37
@@ -27,7 +27,7 @@ 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.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -75,9 +75,7 @@ class PaidMedia(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PaidMedia"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "PaidMedia":
|
||||
"""Converts JSON data to the appropriate :class:`PaidMedia` object, i.e. takes
|
||||
care of selecting the correct subclass.
|
||||
|
||||
@@ -91,12 +89,6 @@ class PaidMedia(TelegramObject):
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
if not data and cls is PaidMedia:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[PaidMedia]] = {
|
||||
cls.PREVIEW: PaidMediaPreview,
|
||||
cls.PHOTO: PaidMediaPhoto,
|
||||
@@ -185,15 +177,10 @@ class PaidMediaPhoto(PaidMedia):
|
||||
self._id_attrs = (self.type, self.photo)
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PaidMediaPhoto"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "PaidMediaPhoto":
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["photo"] = PhotoSize.de_list(data.get("photo"), bot=bot)
|
||||
data["photo"] = de_list_optional(data.get("photo"), PhotoSize, bot)
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
@@ -231,15 +218,10 @@ class PaidMediaVideo(PaidMedia):
|
||||
self._id_attrs = (self.type, self.video)
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PaidMediaVideo"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "PaidMediaVideo":
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["video"] = Video.de_json(data.get("video"), bot=bot)
|
||||
data["video"] = de_json_optional(data.get("video"), Video, bot)
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
@@ -280,15 +262,10 @@ class PaidMediaInfo(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PaidMediaInfo"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "PaidMediaInfo":
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["paid_media"] = PaidMedia.de_list(data.get("paid_media"), bot=bot)
|
||||
data["paid_media"] = de_list_optional(data.get("paid_media"), PaidMedia, bot)
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -329,13 +306,8 @@ class PaidMediaPurchased(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PaidMediaPurchased"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "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,7 +39,7 @@ except ImportError:
|
||||
CRYPTO_INSTALLED = False
|
||||
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.strings import TextEncoding
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram.error import PassportDecryptionError
|
||||
@@ -207,7 +207,7 @@ class EncryptedCredentials(TelegramObject):
|
||||
decrypt_json(self.decrypted_secret, b64decode(self.hash), b64decode(self.data)),
|
||||
self.get_bot(),
|
||||
)
|
||||
return self._decrypted_data # type: ignore[return-value]
|
||||
return self._decrypted_data
|
||||
|
||||
|
||||
class Credentials(TelegramObject):
|
||||
@@ -234,16 +234,11 @@ class Credentials(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["Credentials"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Credentials":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["secure_data"] = SecureData.de_json(data.get("secure_data"), bot=bot)
|
||||
data["secure_data"] = de_json_optional(data.get("secure_data"), SecureData, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -346,30 +341,27 @@ class SecureData(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["SecureData"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "SecureData":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["temporary_registration"] = SecureValue.de_json(
|
||||
data.get("temporary_registration"), bot=bot
|
||||
data["temporary_registration"] = de_json_optional(
|
||||
data.get("temporary_registration"), SecureValue, bot
|
||||
)
|
||||
data["passport_registration"] = SecureValue.de_json(
|
||||
data.get("passport_registration"), bot=bot
|
||||
data["passport_registration"] = de_json_optional(
|
||||
data.get("passport_registration"), SecureValue, bot
|
||||
)
|
||||
data["rental_agreement"] = SecureValue.de_json(data.get("rental_agreement"), bot=bot)
|
||||
data["bank_statement"] = SecureValue.de_json(data.get("bank_statement"), bot=bot)
|
||||
data["utility_bill"] = SecureValue.de_json(data.get("utility_bill"), bot=bot)
|
||||
data["address"] = SecureValue.de_json(data.get("address"), bot=bot)
|
||||
data["identity_card"] = SecureValue.de_json(data.get("identity_card"), bot=bot)
|
||||
data["driver_license"] = SecureValue.de_json(data.get("driver_license"), bot=bot)
|
||||
data["internal_passport"] = SecureValue.de_json(data.get("internal_passport"), bot=bot)
|
||||
data["passport"] = SecureValue.de_json(data.get("passport"), bot=bot)
|
||||
data["personal_details"] = SecureValue.de_json(data.get("personal_details"), bot=bot)
|
||||
data["rental_agreement"] = de_json_optional(data.get("rental_agreement"), SecureValue, bot)
|
||||
data["bank_statement"] = de_json_optional(data.get("bank_statement"), SecureValue, bot)
|
||||
data["utility_bill"] = de_json_optional(data.get("utility_bill"), SecureValue, bot)
|
||||
data["address"] = de_json_optional(data.get("address"), SecureValue, bot)
|
||||
data["identity_card"] = de_json_optional(data.get("identity_card"), SecureValue, bot)
|
||||
data["driver_license"] = de_json_optional(data.get("driver_license"), SecureValue, bot)
|
||||
data["internal_passport"] = de_json_optional(
|
||||
data.get("internal_passport"), SecureValue, bot
|
||||
)
|
||||
data["passport"] = de_json_optional(data.get("passport"), SecureValue, bot)
|
||||
data["personal_details"] = de_json_optional(data.get("personal_details"), SecureValue, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -454,21 +446,16 @@ class SecureValue(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["SecureValue"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "SecureValue":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["data"] = DataCredentials.de_json(data.get("data"), bot=bot)
|
||||
data["front_side"] = FileCredentials.de_json(data.get("front_side"), bot=bot)
|
||||
data["reverse_side"] = FileCredentials.de_json(data.get("reverse_side"), bot=bot)
|
||||
data["selfie"] = FileCredentials.de_json(data.get("selfie"), bot=bot)
|
||||
data["files"] = FileCredentials.de_list(data.get("files"), bot=bot)
|
||||
data["translation"] = FileCredentials.de_list(data.get("translation"), bot=bot)
|
||||
data["data"] = de_json_optional(data.get("data"), DataCredentials, bot)
|
||||
data["front_side"] = de_json_optional(data.get("front_side"), FileCredentials, bot)
|
||||
data["reverse_side"] = de_json_optional(data.get("reverse_side"), FileCredentials, bot)
|
||||
data["selfie"] = de_json_optional(data.get("selfie"), FileCredentials, bot)
|
||||
data["files"] = de_list_optional(data.get("files"), FileCredentials, bot)
|
||||
data["translation"] = de_list_optional(data.get("translation"), FileCredentials, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -25,7 +25,13 @@ from telegram._passport.credentials import decrypt_json
|
||||
from telegram._passport.data import IdDocumentData, PersonalDetails, ResidentialAddress
|
||||
from telegram._passport.passportfile import PassportFile
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.argumentparsing import (
|
||||
de_json_decrypted_optional,
|
||||
de_json_optional,
|
||||
de_list_decrypted_optional,
|
||||
de_list_optional,
|
||||
parse_sequence_arg,
|
||||
)
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -158,10 +164,6 @@ class EncryptedPassportElement(TelegramObject):
|
||||
reverse_side: Optional[PassportFile] = None,
|
||||
selfie: Optional[PassportFile] = None,
|
||||
translation: Optional[Sequence[PassportFile]] = None,
|
||||
# TODO: Remove the credentials argument in 22.0 or later
|
||||
credentials: Optional[ # pylint: disable=unused-argument # noqa: ARG002
|
||||
"Credentials"
|
||||
] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
@@ -194,27 +196,22 @@ class EncryptedPassportElement(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["EncryptedPassportElement"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "EncryptedPassportElement":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["files"] = PassportFile.de_list(data.get("files"), bot) or None
|
||||
data["front_side"] = PassportFile.de_json(data.get("front_side"), bot)
|
||||
data["reverse_side"] = PassportFile.de_json(data.get("reverse_side"), bot)
|
||||
data["selfie"] = PassportFile.de_json(data.get("selfie"), bot)
|
||||
data["translation"] = PassportFile.de_list(data.get("translation"), bot) or None
|
||||
data["files"] = de_list_optional(data.get("files"), PassportFile, bot) or None
|
||||
data["front_side"] = de_json_optional(data.get("front_side"), PassportFile, bot)
|
||||
data["reverse_side"] = de_json_optional(data.get("reverse_side"), PassportFile, bot)
|
||||
data["selfie"] = de_json_optional(data.get("selfie"), PassportFile, bot)
|
||||
data["translation"] = de_list_optional(data.get("translation"), PassportFile, bot) or None
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@classmethod
|
||||
def de_json_decrypted(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"], credentials: "Credentials"
|
||||
) -> Optional["EncryptedPassportElement"]:
|
||||
cls, data: JSONDict, bot: Optional["Bot"], credentials: "Credentials"
|
||||
) -> "EncryptedPassportElement":
|
||||
"""Variant of :meth:`telegram.TelegramObject.de_json` that also takes into account
|
||||
passport credentials.
|
||||
|
||||
@@ -234,8 +231,6 @@ class EncryptedPassportElement(TelegramObject):
|
||||
:class:`telegram.EncryptedPassportElement`:
|
||||
|
||||
"""
|
||||
if not data:
|
||||
return None
|
||||
|
||||
if data["type"] not in ("phone_number", "email"):
|
||||
secure_data = getattr(credentials.secure_data, data["type"])
|
||||
@@ -261,20 +256,21 @@ class EncryptedPassportElement(TelegramObject):
|
||||
data["data"] = ResidentialAddress.de_json(data["data"], bot=bot)
|
||||
|
||||
data["files"] = (
|
||||
PassportFile.de_list_decrypted(data.get("files"), bot, secure_data.files) or None
|
||||
de_list_decrypted_optional(data.get("files"), PassportFile, bot, secure_data.files)
|
||||
or None
|
||||
)
|
||||
data["front_side"] = PassportFile.de_json_decrypted(
|
||||
data.get("front_side"), bot, secure_data.front_side
|
||||
data["front_side"] = de_json_decrypted_optional(
|
||||
data.get("front_side"), PassportFile, bot, secure_data.front_side
|
||||
)
|
||||
data["reverse_side"] = PassportFile.de_json_decrypted(
|
||||
data.get("reverse_side"), bot, secure_data.reverse_side
|
||||
data["reverse_side"] = de_json_decrypted_optional(
|
||||
data.get("reverse_side"), PassportFile, bot, secure_data.reverse_side
|
||||
)
|
||||
data["selfie"] = PassportFile.de_json_decrypted(
|
||||
data.get("selfie"), bot, secure_data.selfie
|
||||
data["selfie"] = de_json_decrypted_optional(
|
||||
data.get("selfie"), PassportFile, bot, secure_data.selfie
|
||||
)
|
||||
data["translation"] = (
|
||||
PassportFile.de_list_decrypted(
|
||||
data.get("translation"), bot, secure_data.translation
|
||||
de_list_decrypted_optional(
|
||||
data.get("translation"), PassportFile, bot, secure_data.translation
|
||||
)
|
||||
or None
|
||||
)
|
||||
|
||||
@@ -23,7 +23,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
from telegram._passport.credentials import EncryptedCredentials
|
||||
from telegram._passport.encryptedpassportelement import EncryptedPassportElement
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -82,17 +82,12 @@ class PassportData(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PassportData"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "PassportData":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["data"] = EncryptedPassportElement.de_list(data.get("data"), bot)
|
||||
data["credentials"] = EncryptedCredentials.de_json(data.get("credentials"), bot)
|
||||
data["data"] = de_list_optional(data.get("data"), EncryptedPassportElement, bot)
|
||||
data["credentials"] = de_json_optional(data.get("credentials"), EncryptedCredentials, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
# pylint: disable=redefined-builtin
|
||||
"""This module contains the classes that represent Telegram PassportElementError."""
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import Optional
|
||||
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
|
||||
|
||||
class PassportElementError(TelegramObject):
|
||||
@@ -168,23 +168,30 @@ class PassportElementErrorFiles(PassportElementError):
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of
|
||||
``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``,
|
||||
``"passport_registration"``, ``"temporary_registration"``.
|
||||
file_hashes (list[:obj:`str`]): List of base64-encoded file hashes.
|
||||
file_hashes (Sequence[:obj:`str`]): List of base64-encoded file hashes.
|
||||
|
||||
.. versionchanged:: 22.0
|
||||
|sequenceargs|
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of
|
||||
``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``,
|
||||
``"passport_registration"``, ``"temporary_registration"``.
|
||||
file_hashes (tuple[:obj:`str`]): List of base64-encoded file hashes.
|
||||
|
||||
.. versionchanged:: 22.0
|
||||
|tupleclassattrs|
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("_file_hashes",)
|
||||
__slots__ = ("file_hashes",)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type: str,
|
||||
file_hashes: list[str],
|
||||
file_hashes: Sequence[str],
|
||||
message: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -192,32 +199,9 @@ class PassportElementErrorFiles(PassportElementError):
|
||||
# Required
|
||||
super().__init__("files", type, message, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self._file_hashes: list[str] = file_hashes
|
||||
self.file_hashes: tuple[str, ...] = parse_sequence_arg(file_hashes)
|
||||
|
||||
self._id_attrs = (self.source, self.type, self.message, *tuple(file_hashes))
|
||||
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict` for details."""
|
||||
data = super().to_dict(recursive)
|
||||
data["file_hashes"] = self._file_hashes
|
||||
return data
|
||||
|
||||
@property
|
||||
def file_hashes(self) -> list[str]:
|
||||
"""List of base64-encoded file hashes.
|
||||
|
||||
.. deprecated:: 20.6
|
||||
This attribute will return a tuple instead of a list in future major versions.
|
||||
"""
|
||||
warn(
|
||||
PTBDeprecationWarning(
|
||||
"20.6",
|
||||
"The attribute `file_hashes` will return a tuple instead of a list in future major"
|
||||
" versions.",
|
||||
),
|
||||
stacklevel=2,
|
||||
)
|
||||
return self._file_hashes
|
||||
self._id_attrs = (self.source, self.type, self.message, self.file_hashes)
|
||||
|
||||
|
||||
class PassportElementErrorFrontSide(PassportElementError):
|
||||
@@ -386,7 +370,10 @@ class PassportElementErrorTranslationFiles(PassportElementError):
|
||||
one of ``"passport"``, ``"driver_license"``, ``"identity_card"``,
|
||||
``"internal_passport"``, ``"utility_bill"``, ``"bank_statement"``,
|
||||
``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``.
|
||||
file_hashes (list[:obj:`str`]): List of base64-encoded file hashes.
|
||||
file_hashes (Sequence[:obj:`str`]): List of base64-encoded file hashes.
|
||||
|
||||
.. versionchanged:: 22.0
|
||||
|sequenceargs|
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
Attributes:
|
||||
@@ -394,16 +381,20 @@ class PassportElementErrorTranslationFiles(PassportElementError):
|
||||
one of ``"passport"``, ``"driver_license"``, ``"identity_card"``,
|
||||
``"internal_passport"``, ``"utility_bill"``, ``"bank_statement"``,
|
||||
``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``.
|
||||
file_hashes (tuple[:obj:`str`]): List of base64-encoded file hashes.
|
||||
|
||||
.. versionchanged:: 22.0
|
||||
|tupleclassattrs|
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("_file_hashes",)
|
||||
__slots__ = ("file_hashes",)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type: str,
|
||||
file_hashes: list[str],
|
||||
file_hashes: Sequence[str],
|
||||
message: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
@@ -411,33 +402,9 @@ class PassportElementErrorTranslationFiles(PassportElementError):
|
||||
# Required
|
||||
super().__init__("translation_files", type, message, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self._file_hashes: list[str] = file_hashes
|
||||
self.file_hashes: tuple[str, ...] = parse_sequence_arg(file_hashes)
|
||||
|
||||
self._id_attrs = (self.source, self.type, self.message, *tuple(file_hashes))
|
||||
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict` for details."""
|
||||
data = super().to_dict(recursive)
|
||||
data["file_hashes"] = self._file_hashes
|
||||
return data
|
||||
|
||||
@property
|
||||
def file_hashes(self) -> list[str]:
|
||||
"""List of base64-encoded file hashes.
|
||||
|
||||
.. deprecated:: 20.6
|
||||
This attribute will return a tuple instead of a list in future major versions.
|
||||
"""
|
||||
warn(
|
||||
PTBDeprecationWarning(
|
||||
"20.6",
|
||||
"The attribute `file_hashes` will return a tuple instead of a list in future major"
|
||||
" versions. See the stability policy:"
|
||||
" https://docs.python-telegram-bot.org/en/stable/stability_policy.html",
|
||||
),
|
||||
stacklevel=2,
|
||||
)
|
||||
return self._file_hashes
|
||||
self._id_attrs = (self.source, self.type, self.message, self.file_hashes)
|
||||
|
||||
|
||||
class PassportElementErrorUnspecified(PassportElementError):
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Encrypted PassportFile."""
|
||||
|
||||
import datetime as dtm
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot, File, FileCredentials
|
||||
@@ -45,11 +45,11 @@ class PassportFile(TelegramObject):
|
||||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
file_size (:obj:`int`): File size in bytes.
|
||||
file_date (:obj:`int`): Unix time when the file was uploaded.
|
||||
file_date (:class:`datetime.datetime`): Time when the file was uploaded.
|
||||
|
||||
.. deprecated:: 20.6
|
||||
This argument will only accept a datetime instead of an integer in future
|
||||
major versions.
|
||||
.. versionchanged:: 22.0
|
||||
Accepts only :class:`datetime.datetime` instead of :obj:`int`.
|
||||
|datetime_localization|
|
||||
|
||||
Attributes:
|
||||
file_id (:obj:`str`): Identifier for this file, which can be used to download
|
||||
@@ -58,11 +58,16 @@ class PassportFile(TelegramObject):
|
||||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
file_size (:obj:`int`): File size in bytes.
|
||||
file_date (:class:`datetime.datetime`): Time when the file was uploaded.
|
||||
|
||||
.. versionchanged:: 22.0
|
||||
Returns :class:`datetime.datetime` instead of :obj:`int`.
|
||||
|datetime_localization|
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"_credentials",
|
||||
"_file_date",
|
||||
"file_date",
|
||||
"file_id",
|
||||
"file_size",
|
||||
"file_unique_id",
|
||||
@@ -72,7 +77,7 @@ class PassportFile(TelegramObject):
|
||||
self,
|
||||
file_id: str,
|
||||
file_unique_id: str,
|
||||
file_date: int,
|
||||
file_date: dtm.datetime,
|
||||
file_size: int,
|
||||
credentials: Optional["FileCredentials"] = None,
|
||||
*,
|
||||
@@ -84,7 +89,7 @@ class PassportFile(TelegramObject):
|
||||
self.file_id: str = file_id
|
||||
self.file_unique_id: str = file_unique_id
|
||||
self.file_size: int = file_size
|
||||
self._file_date: int = file_date
|
||||
self.file_date: dtm.datetime = file_date
|
||||
# Optionals
|
||||
|
||||
self._credentials: Optional[FileCredentials] = credentials
|
||||
@@ -93,33 +98,21 @@ class PassportFile(TelegramObject):
|
||||
|
||||
self._freeze()
|
||||
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict` for details."""
|
||||
data = super().to_dict(recursive)
|
||||
data["file_date"] = self._file_date
|
||||
return data
|
||||
@classmethod
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "PassportFile":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
@property
|
||||
def file_date(self) -> int:
|
||||
""":obj:`int`: Unix time when the file was uploaded.
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
data["file_date"] = from_timestamp(data.get("file_date"), tzinfo=loc_tzinfo)
|
||||
|
||||
.. deprecated:: 20.6
|
||||
This attribute will return a datetime instead of a integer in future major versions.
|
||||
"""
|
||||
warn(
|
||||
PTBDeprecationWarning(
|
||||
"20.6",
|
||||
"The attribute `file_date` will return a datetime instead of an integer in future"
|
||||
" major versions.",
|
||||
),
|
||||
stacklevel=2,
|
||||
)
|
||||
return self._file_date
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@classmethod
|
||||
def de_json_decrypted(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"], credentials: "FileCredentials"
|
||||
) -> Optional["PassportFile"]:
|
||||
cls, data: JSONDict, bot: Optional["Bot"], credentials: "FileCredentials"
|
||||
) -> "PassportFile":
|
||||
"""Variant of :meth:`telegram.TelegramObject.de_json` that also takes into account
|
||||
passport credentials.
|
||||
|
||||
@@ -141,9 +134,6 @@ class PassportFile(TelegramObject):
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["credentials"] = credentials
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
@@ -151,10 +141,10 @@ class PassportFile(TelegramObject):
|
||||
@classmethod
|
||||
def de_list_decrypted(
|
||||
cls,
|
||||
data: Optional[list[JSONDict]],
|
||||
data: list[JSONDict],
|
||||
bot: Optional["Bot"],
|
||||
credentials: list["FileCredentials"],
|
||||
) -> tuple[Optional["PassportFile"], ...]:
|
||||
) -> tuple["PassportFile", ...]:
|
||||
"""Variant of :meth:`telegram.TelegramObject.de_list` that also takes into account
|
||||
passport credentials.
|
||||
|
||||
@@ -179,16 +169,12 @@ class PassportFile(TelegramObject):
|
||||
tuple[:class:`telegram.PassportFile`]:
|
||||
|
||||
"""
|
||||
if not data:
|
||||
return ()
|
||||
|
||||
return tuple(
|
||||
obj
|
||||
for obj in (
|
||||
cls.de_json_decrypted(passport_file, bot, credentials[i])
|
||||
for i, passport_file in enumerate(data)
|
||||
)
|
||||
if obj is not None
|
||||
)
|
||||
|
||||
async def get_file(
|
||||
|
||||
@@ -22,6 +22,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._payment.shippingaddress import ShippingAddress
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -71,15 +72,12 @@ class OrderInfo(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["OrderInfo"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "OrderInfo":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return cls()
|
||||
|
||||
data["shipping_address"] = ShippingAddress.de_json(data.get("shipping_address"), bot)
|
||||
data["shipping_address"] = de_json_optional(
|
||||
data.get("shipping_address"), ShippingAddress, bot
|
||||
)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -23,6 +23,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
from telegram._payment.orderinfo import OrderInfo
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
|
||||
@@ -110,17 +111,12 @@ class PreCheckoutQuery(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PreCheckoutQuery"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "PreCheckoutQuery":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["from_user"] = User.de_json(data.pop("from", None), bot)
|
||||
data["order_info"] = OrderInfo.de_json(data.get("order_info"), bot)
|
||||
data["from_user"] = de_json_optional(data.pop("from", None), User, bot)
|
||||
data["order_info"] = de_json_optional(data.get("order_info"), OrderInfo, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
from telegram._payment.shippingaddress import ShippingAddress
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
|
||||
@@ -78,17 +79,14 @@ class ShippingQuery(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ShippingQuery"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ShippingQuery":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["from_user"] = User.de_json(data.pop("from", None), bot)
|
||||
data["shipping_address"] = ShippingAddress.de_json(data.get("shipping_address"), bot)
|
||||
data["from_user"] = de_json_optional(data.pop("from", None), User, bot)
|
||||
data["shipping_address"] = de_json_optional(
|
||||
data.get("shipping_address"), ShippingAddress, bot
|
||||
)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
from telegram._chat import Chat
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._user import User
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -105,16 +106,11 @@ class AffiliateInfo(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["AffiliateInfo"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "AffiliateInfo":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["affiliate_user"] = User.de_json(data.get("affiliate_user"), bot)
|
||||
data["affiliate_chat"] = Chat.de_json(data.get("affiliate_chat"), bot)
|
||||
data["affiliate_user"] = de_json_optional(data.get("affiliate_user"), User, bot)
|
||||
data["affiliate_chat"] = de_json_optional(data.get("affiliate_chat"), Chat, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user