mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-19 15:45:13 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1e4f31f1bb | |||
| 3464f24129 | |||
| 9323caf2b8 | |||
| 77c25931a9 | |||
| b75948ede4 | |||
| b0d22acedb | |||
| 35a48f82f4 | |||
| 5d73132838 | |||
| 7c23087d08 | |||
| 2d5f4a68bb | |||
| f9f1533c40 | |||
| dfb0ae3747 | |||
| 64006aa7ae | |||
| 69ddc47a6e | |||
| a2150b3751 | |||
| 79acc1ae53 | |||
| 4cdb1a0cf7 | |||
| 6319f4bae1 | |||
| d7e063dbad | |||
| 5dd7b8f1e2 | |||
| 61b87ba318 | |||
| dd592cdd7c | |||
| f57dd52100 | |||
| 16605c54d7 |
@@ -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,17 +1,23 @@
|
||||
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']
|
||||
@@ -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,6 +7,8 @@ on:
|
||||
paths:
|
||||
- .github/workflows/docs-linkcheck.yml
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
test-sphinx-build:
|
||||
name: test-sphinx-linkcheck
|
||||
@@ -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@4db96194c378173c656ce18a155ffc14a9fc4355 # v5.2.2
|
||||
- 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@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor
|
||||
@@ -4,6 +4,8 @@ on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
pre-commit-ci:
|
||||
permissions:
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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>`_
|
||||
|
||||
+61
-1
@@ -4,6 +4,66 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Version 21.11.1
|
||||
===============
|
||||
|
||||
*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 +146,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
|
||||
============
|
||||
|
||||
+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
|
||||
|
||||
+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
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -111,6 +111,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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
||||
+1
-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" }
|
||||
]
|
||||
|
||||
@@ -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,
|
||||
|
||||
+247
-93
@@ -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
|
||||
@@ -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,
|
||||
@@ -4523,8 +4648,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 +4857,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 +4970,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 +5065,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 +5197,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 +5252,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 +5572,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 +5986,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 +6065,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 +6112,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 +6584,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 +6687,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 +6984,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 +6998,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 +7318,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 +7381,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 +7548,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 +7704,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 +7994,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 +8014,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 +8108,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 +8120,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 +8271,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 +8295,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 +8322,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 +8416,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 +8518,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 +9106,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 +9145,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 +9231,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 +9273,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 +9300,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 +9398,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 +9537,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 +9588,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 +9708,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 +9729,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 +9767,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 +9820,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 +9831,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 +9842,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 +9855,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 +9869,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 +9912,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 +9924,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 +9947,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 +9989,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 +10030,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 +10069,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
|
||||
@@ -126,17 +127,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 +141,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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -97,14 +98,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]
|
||||
|
||||
|
||||
+121
-88
@@ -65,7 +65,7 @@ from telegram._shared import ChatShared, UsersShared
|
||||
from telegram._story import Story
|
||||
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.defaultvalue import DEFAULT_NONE, DefaultValue
|
||||
from telegram._utils.entities import parse_message_entities, parse_message_entity
|
||||
@@ -77,6 +77,7 @@ from telegram._utils.types import (
|
||||
MarkdownVersion,
|
||||
ODVInput,
|
||||
ReplyMarkup,
|
||||
TimePeriod,
|
||||
)
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram._videochat import (
|
||||
@@ -191,9 +192,6 @@ class MaybeInaccessibleMessage(TelegramObject):
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
if cls is MaybeInaccessibleMessage:
|
||||
if data["date"] == 0:
|
||||
return InaccessibleMessage.de_json(data=data, bot=bot)
|
||||
@@ -206,9 +204,9 @@ class MaybeInaccessibleMessage(TelegramObject):
|
||||
if data["date"] == 0:
|
||||
data["date"] = ZERO_DATE
|
||||
else:
|
||||
data["date"] = from_timestamp(data["date"], tzinfo=loc_tzinfo)
|
||||
data["date"] = from_timestamp(data.get("date"), tzinfo=loc_tzinfo)
|
||||
|
||||
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, api_kwargs=api_kwargs)
|
||||
|
||||
|
||||
@@ -1251,83 +1249,100 @@ class Message(MaybeInaccessibleMessage):
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["Message"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Message":
|
||||
"""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["from_user"] = User.de_json(data.pop("from", None), bot)
|
||||
data["sender_chat"] = Chat.de_json(data.get("sender_chat"), bot)
|
||||
data["entities"] = MessageEntity.de_list(data.get("entities"), bot)
|
||||
data["caption_entities"] = MessageEntity.de_list(data.get("caption_entities"), bot)
|
||||
data["reply_to_message"] = Message.de_json(data.get("reply_to_message"), bot)
|
||||
data["from_user"] = de_json_optional(data.pop("from", None), User, bot)
|
||||
data["sender_chat"] = de_json_optional(data.get("sender_chat"), Chat, bot)
|
||||
data["entities"] = de_list_optional(data.get("entities"), MessageEntity, bot)
|
||||
data["caption_entities"] = de_list_optional(
|
||||
data.get("caption_entities"), MessageEntity, bot
|
||||
)
|
||||
data["reply_to_message"] = de_json_optional(data.get("reply_to_message"), Message, bot)
|
||||
data["edit_date"] = from_timestamp(data.get("edit_date"), tzinfo=loc_tzinfo)
|
||||
data["audio"] = Audio.de_json(data.get("audio"), bot)
|
||||
data["document"] = Document.de_json(data.get("document"), bot)
|
||||
data["animation"] = Animation.de_json(data.get("animation"), bot)
|
||||
data["game"] = Game.de_json(data.get("game"), bot)
|
||||
data["photo"] = PhotoSize.de_list(data.get("photo"), bot)
|
||||
data["sticker"] = Sticker.de_json(data.get("sticker"), bot)
|
||||
data["story"] = Story.de_json(data.get("story"), bot)
|
||||
data["video"] = Video.de_json(data.get("video"), bot)
|
||||
data["voice"] = Voice.de_json(data.get("voice"), bot)
|
||||
data["video_note"] = VideoNote.de_json(data.get("video_note"), bot)
|
||||
data["contact"] = Contact.de_json(data.get("contact"), bot)
|
||||
data["location"] = Location.de_json(data.get("location"), bot)
|
||||
data["venue"] = Venue.de_json(data.get("venue"), bot)
|
||||
data["new_chat_members"] = User.de_list(data.get("new_chat_members"), bot)
|
||||
data["left_chat_member"] = User.de_json(data.get("left_chat_member"), bot)
|
||||
data["new_chat_photo"] = PhotoSize.de_list(data.get("new_chat_photo"), bot)
|
||||
data["message_auto_delete_timer_changed"] = MessageAutoDeleteTimerChanged.de_json(
|
||||
data.get("message_auto_delete_timer_changed"), bot
|
||||
data["audio"] = de_json_optional(data.get("audio"), Audio, bot)
|
||||
data["document"] = de_json_optional(data.get("document"), Document, bot)
|
||||
data["animation"] = de_json_optional(data.get("animation"), Animation, bot)
|
||||
data["game"] = de_json_optional(data.get("game"), Game, bot)
|
||||
data["photo"] = de_list_optional(data.get("photo"), PhotoSize, bot)
|
||||
data["sticker"] = de_json_optional(data.get("sticker"), Sticker, bot)
|
||||
data["story"] = de_json_optional(data.get("story"), Story, bot)
|
||||
data["video"] = de_json_optional(data.get("video"), Video, bot)
|
||||
data["voice"] = de_json_optional(data.get("voice"), Voice, bot)
|
||||
data["video_note"] = de_json_optional(data.get("video_note"), VideoNote, bot)
|
||||
data["contact"] = de_json_optional(data.get("contact"), Contact, bot)
|
||||
data["location"] = de_json_optional(data.get("location"), Location, bot)
|
||||
data["venue"] = de_json_optional(data.get("venue"), Venue, bot)
|
||||
data["new_chat_members"] = de_list_optional(data.get("new_chat_members"), User, bot)
|
||||
data["left_chat_member"] = de_json_optional(data.get("left_chat_member"), User, bot)
|
||||
data["new_chat_photo"] = de_list_optional(data.get("new_chat_photo"), PhotoSize, bot)
|
||||
data["message_auto_delete_timer_changed"] = de_json_optional(
|
||||
data.get("message_auto_delete_timer_changed"), MessageAutoDeleteTimerChanged, bot
|
||||
)
|
||||
data["pinned_message"] = MaybeInaccessibleMessage.de_json(data.get("pinned_message"), bot)
|
||||
data["invoice"] = Invoice.de_json(data.get("invoice"), bot)
|
||||
data["successful_payment"] = SuccessfulPayment.de_json(data.get("successful_payment"), bot)
|
||||
data["passport_data"] = PassportData.de_json(data.get("passport_data"), bot)
|
||||
data["poll"] = Poll.de_json(data.get("poll"), bot)
|
||||
data["dice"] = Dice.de_json(data.get("dice"), bot)
|
||||
data["via_bot"] = User.de_json(data.get("via_bot"), bot)
|
||||
data["proximity_alert_triggered"] = ProximityAlertTriggered.de_json(
|
||||
data.get("proximity_alert_triggered"), bot
|
||||
data["pinned_message"] = de_json_optional(
|
||||
data.get("pinned_message"), MaybeInaccessibleMessage, bot
|
||||
)
|
||||
data["reply_markup"] = InlineKeyboardMarkup.de_json(data.get("reply_markup"), bot)
|
||||
data["video_chat_scheduled"] = VideoChatScheduled.de_json(
|
||||
data.get("video_chat_scheduled"), bot
|
||||
data["invoice"] = de_json_optional(data.get("invoice"), Invoice, bot)
|
||||
data["successful_payment"] = de_json_optional(
|
||||
data.get("successful_payment"), SuccessfulPayment, bot
|
||||
)
|
||||
data["video_chat_started"] = VideoChatStarted.de_json(data.get("video_chat_started"), bot)
|
||||
data["video_chat_ended"] = VideoChatEnded.de_json(data.get("video_chat_ended"), bot)
|
||||
data["video_chat_participants_invited"] = VideoChatParticipantsInvited.de_json(
|
||||
data.get("video_chat_participants_invited"), bot
|
||||
data["passport_data"] = de_json_optional(data.get("passport_data"), PassportData, bot)
|
||||
data["poll"] = de_json_optional(data.get("poll"), Poll, bot)
|
||||
data["dice"] = de_json_optional(data.get("dice"), Dice, bot)
|
||||
data["via_bot"] = de_json_optional(data.get("via_bot"), User, bot)
|
||||
data["proximity_alert_triggered"] = de_json_optional(
|
||||
data.get("proximity_alert_triggered"), ProximityAlertTriggered, bot
|
||||
)
|
||||
data["web_app_data"] = WebAppData.de_json(data.get("web_app_data"), bot)
|
||||
data["forum_topic_closed"] = ForumTopicClosed.de_json(data.get("forum_topic_closed"), bot)
|
||||
data["forum_topic_created"] = ForumTopicCreated.de_json(
|
||||
data.get("forum_topic_created"), bot
|
||||
data["reply_markup"] = de_json_optional(
|
||||
data.get("reply_markup"), InlineKeyboardMarkup, bot
|
||||
)
|
||||
data["forum_topic_reopened"] = ForumTopicReopened.de_json(
|
||||
data.get("forum_topic_reopened"), bot
|
||||
data["video_chat_scheduled"] = de_json_optional(
|
||||
data.get("video_chat_scheduled"), VideoChatScheduled, bot
|
||||
)
|
||||
data["forum_topic_edited"] = ForumTopicEdited.de_json(data.get("forum_topic_edited"), bot)
|
||||
data["general_forum_topic_hidden"] = GeneralForumTopicHidden.de_json(
|
||||
data.get("general_forum_topic_hidden"), bot
|
||||
data["video_chat_started"] = de_json_optional(
|
||||
data.get("video_chat_started"), VideoChatStarted, bot
|
||||
)
|
||||
data["general_forum_topic_unhidden"] = GeneralForumTopicUnhidden.de_json(
|
||||
data.get("general_forum_topic_unhidden"), bot
|
||||
data["video_chat_ended"] = de_json_optional(
|
||||
data.get("video_chat_ended"), VideoChatEnded, bot
|
||||
)
|
||||
data["write_access_allowed"] = WriteAccessAllowed.de_json(
|
||||
data.get("write_access_allowed"), bot
|
||||
data["video_chat_participants_invited"] = de_json_optional(
|
||||
data.get("video_chat_participants_invited"), VideoChatParticipantsInvited, bot
|
||||
)
|
||||
data["web_app_data"] = de_json_optional(data.get("web_app_data"), WebAppData, bot)
|
||||
data["forum_topic_closed"] = de_json_optional(
|
||||
data.get("forum_topic_closed"), ForumTopicClosed, bot
|
||||
)
|
||||
data["forum_topic_created"] = de_json_optional(
|
||||
data.get("forum_topic_created"), ForumTopicCreated, bot
|
||||
)
|
||||
data["forum_topic_reopened"] = de_json_optional(
|
||||
data.get("forum_topic_reopened"), ForumTopicReopened, bot
|
||||
)
|
||||
data["forum_topic_edited"] = de_json_optional(
|
||||
data.get("forum_topic_edited"), ForumTopicEdited, bot
|
||||
)
|
||||
data["general_forum_topic_hidden"] = de_json_optional(
|
||||
data.get("general_forum_topic_hidden"), GeneralForumTopicHidden, bot
|
||||
)
|
||||
data["general_forum_topic_unhidden"] = de_json_optional(
|
||||
data.get("general_forum_topic_unhidden"), GeneralForumTopicUnhidden, bot
|
||||
)
|
||||
data["write_access_allowed"] = de_json_optional(
|
||||
data.get("write_access_allowed"), WriteAccessAllowed, bot
|
||||
)
|
||||
data["users_shared"] = de_json_optional(data.get("users_shared"), UsersShared, bot)
|
||||
data["chat_shared"] = de_json_optional(data.get("chat_shared"), ChatShared, bot)
|
||||
data["chat_background_set"] = de_json_optional(
|
||||
data.get("chat_background_set"), ChatBackground, bot
|
||||
)
|
||||
data["paid_media"] = de_json_optional(data.get("paid_media"), PaidMediaInfo, bot)
|
||||
data["refunded_payment"] = de_json_optional(
|
||||
data.get("refunded_payment"), RefundedPayment, bot
|
||||
)
|
||||
data["users_shared"] = UsersShared.de_json(data.get("users_shared"), bot)
|
||||
data["chat_shared"] = ChatShared.de_json(data.get("chat_shared"), bot)
|
||||
data["chat_background_set"] = ChatBackground.de_json(data.get("chat_background_set"), bot)
|
||||
data["paid_media"] = PaidMediaInfo.de_json(data.get("paid_media"), bot)
|
||||
data["refunded_payment"] = RefundedPayment.de_json(data.get("refunded_payment"), bot)
|
||||
|
||||
# Unfortunately, this needs to be here due to cyclic imports
|
||||
from telegram._giveaway import ( # pylint: disable=import-outside-toplevel
|
||||
@@ -1344,19 +1359,27 @@ class Message(MaybeInaccessibleMessage):
|
||||
TextQuote,
|
||||
)
|
||||
|
||||
data["giveaway"] = Giveaway.de_json(data.get("giveaway"), bot)
|
||||
data["giveaway_completed"] = GiveawayCompleted.de_json(data.get("giveaway_completed"), bot)
|
||||
data["giveaway_created"] = GiveawayCreated.de_json(data.get("giveaway_created"), bot)
|
||||
data["giveaway_winners"] = GiveawayWinners.de_json(data.get("giveaway_winners"), bot)
|
||||
data["link_preview_options"] = LinkPreviewOptions.de_json(
|
||||
data.get("link_preview_options"), bot
|
||||
data["giveaway"] = de_json_optional(data.get("giveaway"), Giveaway, bot)
|
||||
data["giveaway_completed"] = de_json_optional(
|
||||
data.get("giveaway_completed"), GiveawayCompleted, bot
|
||||
)
|
||||
data["external_reply"] = ExternalReplyInfo.de_json(data.get("external_reply"), bot)
|
||||
data["quote"] = TextQuote.de_json(data.get("quote"), bot)
|
||||
data["forward_origin"] = MessageOrigin.de_json(data.get("forward_origin"), bot)
|
||||
data["reply_to_story"] = Story.de_json(data.get("reply_to_story"), bot)
|
||||
data["boost_added"] = ChatBoostAdded.de_json(data.get("boost_added"), bot)
|
||||
data["sender_business_bot"] = User.de_json(data.get("sender_business_bot"), bot)
|
||||
data["giveaway_created"] = de_json_optional(
|
||||
data.get("giveaway_created"), GiveawayCreated, bot
|
||||
)
|
||||
data["giveaway_winners"] = de_json_optional(
|
||||
data.get("giveaway_winners"), GiveawayWinners, bot
|
||||
)
|
||||
data["link_preview_options"] = de_json_optional(
|
||||
data.get("link_preview_options"), LinkPreviewOptions, bot
|
||||
)
|
||||
data["external_reply"] = de_json_optional(
|
||||
data.get("external_reply"), ExternalReplyInfo, bot
|
||||
)
|
||||
data["quote"] = de_json_optional(data.get("quote"), TextQuote, bot)
|
||||
data["forward_origin"] = de_json_optional(data.get("forward_origin"), MessageOrigin, bot)
|
||||
data["reply_to_story"] = de_json_optional(data.get("reply_to_story"), Story, bot)
|
||||
data["boost_added"] = de_json_optional(data.get("boost_added"), ChatBoostAdded, bot)
|
||||
data["sender_business_bot"] = de_json_optional(data.get("sender_business_bot"), User, bot)
|
||||
|
||||
api_kwargs = {}
|
||||
# This is a deprecated field that TG still returns for backwards compatibility
|
||||
@@ -2210,7 +2233,7 @@ class Message(MaybeInaccessibleMessage):
|
||||
async def reply_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,
|
||||
@@ -2384,7 +2407,7 @@ class Message(MaybeInaccessibleMessage):
|
||||
async def reply_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,
|
||||
@@ -2552,7 +2575,7 @@ class Message(MaybeInaccessibleMessage):
|
||||
async def reply_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,
|
||||
@@ -2569,6 +2592,8 @@ class Message(MaybeInaccessibleMessage):
|
||||
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,
|
||||
@@ -2638,6 +2663,8 @@ class Message(MaybeInaccessibleMessage):
|
||||
message_thread_id=message_thread_id,
|
||||
has_spoiler=has_spoiler,
|
||||
thumbnail=thumbnail,
|
||||
cover=cover,
|
||||
start_timestamp=start_timestamp,
|
||||
business_connection_id=self.business_connection_id,
|
||||
message_effect_id=message_effect_id,
|
||||
allow_paid_broadcast=allow_paid_broadcast,
|
||||
@@ -2647,7 +2674,7 @@ class Message(MaybeInaccessibleMessage):
|
||||
async def reply_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,
|
||||
@@ -2728,7 +2755,7 @@ class Message(MaybeInaccessibleMessage):
|
||||
async def reply_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,
|
||||
@@ -2814,7 +2841,7 @@ class Message(MaybeInaccessibleMessage):
|
||||
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,
|
||||
@@ -3076,7 +3103,7 @@ class Message(MaybeInaccessibleMessage):
|
||||
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,
|
||||
@@ -3359,9 +3386,9 @@ class Message(MaybeInaccessibleMessage):
|
||||
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,
|
||||
@@ -3483,6 +3510,7 @@ class Message(MaybeInaccessibleMessage):
|
||||
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,
|
||||
@@ -3517,6 +3545,7 @@ class Message(MaybeInaccessibleMessage):
|
||||
chat_id=chat_id,
|
||||
from_chat_id=self.chat_id,
|
||||
message_id=self.message_id,
|
||||
video_start_timestamp=video_start_timestamp,
|
||||
disable_notification=disable_notification,
|
||||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
@@ -3540,6 +3569,7 @@ class Message(MaybeInaccessibleMessage):
|
||||
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,
|
||||
@@ -3570,6 +3600,7 @@ class Message(MaybeInaccessibleMessage):
|
||||
from_chat_id=self.chat_id,
|
||||
message_id=self.message_id,
|
||||
caption=caption,
|
||||
video_start_timestamp=video_start_timestamp,
|
||||
parse_mode=parse_mode,
|
||||
caption_entities=caption_entities,
|
||||
disable_notification=disable_notification,
|
||||
@@ -3602,6 +3633,7 @@ class Message(MaybeInaccessibleMessage):
|
||||
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,
|
||||
@@ -3652,6 +3684,7 @@ class Message(MaybeInaccessibleMessage):
|
||||
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,
|
||||
@@ -3958,7 +3991,7 @@ class Message(MaybeInaccessibleMessage):
|
||||
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,
|
||||
|
||||
@@ -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:
|
||||
@@ -194,27 +200,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 +235,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 +260,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)
|
||||
|
||||
|
||||
@@ -118,8 +118,8 @@ class PassportFile(TelegramObject):
|
||||
|
||||
@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 +141,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 +148,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 +176,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)
|
||||
|
||||
@@ -68,9 +68,7 @@ class RevenueWithdrawalState(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["RevenueWithdrawalState"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "RevenueWithdrawalState":
|
||||
"""Converts JSON data to the appropriate :class:`RevenueWithdrawalState` object, i.e. takes
|
||||
care of selecting the correct subclass.
|
||||
|
||||
@@ -84,9 +82,6 @@ class RevenueWithdrawalState(TelegramObject):
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if (cls is RevenueWithdrawalState and not data) or data is None:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[RevenueWithdrawalState]] = {
|
||||
cls.PENDING: RevenueWithdrawalStatePending,
|
||||
cls.SUCCEEDED: RevenueWithdrawalStateSucceeded,
|
||||
@@ -156,14 +151,11 @@ class RevenueWithdrawalStateSucceeded(RevenueWithdrawalState):
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["RevenueWithdrawalStateSucceeded"]:
|
||||
cls, data: JSONDict, bot: Optional["Bot"] = None
|
||||
) -> "RevenueWithdrawalStateSucceeded":
|
||||
"""See :meth:`telegram.RevenueWithdrawalState.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", None), tzinfo=loc_tzinfo)
|
||||
|
||||
@@ -24,7 +24,7 @@ from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
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.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
@@ -36,6 +36,9 @@ if TYPE_CHECKING:
|
||||
|
||||
class StarTransaction(TelegramObject):
|
||||
"""Describes a Telegram Star transaction.
|
||||
Note that if the buyer initiates a chargeback with the payment provider from whom they
|
||||
acquired Stars (e.g., Apple, Google) following this transaction, the refunded Stars will be
|
||||
deducted from the bot's balance. This is outside of Telegram's control.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`id`, :attr:`source`, and :attr:`receiver` are equal.
|
||||
@@ -112,21 +115,16 @@ class StarTransaction(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["StarTransaction"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "StarTransaction":
|
||||
"""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", None), tzinfo=loc_tzinfo)
|
||||
|
||||
data["source"] = TransactionPartner.de_json(data.get("source"), bot)
|
||||
data["receiver"] = TransactionPartner.de_json(data.get("receiver"), bot)
|
||||
data["source"] = de_json_optional(data.get("source"), TransactionPartner, bot)
|
||||
data["receiver"] = de_json_optional(data.get("receiver"), TransactionPartner, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -159,14 +157,9 @@ class StarTransactions(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["StarTransactions"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "StarTransactions":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
data["transactions"] = StarTransaction.de_list(data.get("transactions"), bot)
|
||||
data["transactions"] = de_list_optional(data.get("transactions"), StarTransaction, bot)
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -23,12 +23,13 @@ from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Final, Optional
|
||||
|
||||
from telegram import constants
|
||||
from telegram._chat import Chat
|
||||
from telegram._gifts import Gift
|
||||
from telegram._paidmedia import PaidMedia
|
||||
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
|
||||
|
||||
from .affiliateinfo import AffiliateInfo
|
||||
@@ -43,6 +44,7 @@ class TransactionPartner(TelegramObject):
|
||||
transactions. Currently, it can be one of:
|
||||
|
||||
* :class:`TransactionPartnerUser`
|
||||
* :class:`TransactionPartnerChat`
|
||||
* :class:`TransactionPartnerAffiliateProgram`
|
||||
* :class:`TransactionPartnerFragment`
|
||||
* :class:`TransactionPartnerTelegramAds`
|
||||
@@ -54,6 +56,9 @@ class TransactionPartner(TelegramObject):
|
||||
|
||||
.. versionadded:: 21.4
|
||||
|
||||
..versionchanged:: 21.11
|
||||
Added :class:`TransactionPartnerChat`
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): The type of the transaction partner.
|
||||
|
||||
@@ -68,6 +73,11 @@ class TransactionPartner(TelegramObject):
|
||||
|
||||
.. versionadded:: 21.9
|
||||
"""
|
||||
CHAT: Final[str] = constants.TransactionPartnerType.CHAT
|
||||
""":const:`telegram.constants.TransactionPartnerType.CHAT`
|
||||
|
||||
.. versionadded:: 21.11
|
||||
"""
|
||||
FRAGMENT: Final[str] = constants.TransactionPartnerType.FRAGMENT
|
||||
""":const:`telegram.constants.TransactionPartnerType.FRAGMENT`"""
|
||||
OTHER: Final[str] = constants.TransactionPartnerType.OTHER
|
||||
@@ -87,9 +97,7 @@ class TransactionPartner(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["TransactionPartner"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "TransactionPartner":
|
||||
"""Converts JSON data to the appropriate :class:`TransactionPartner` object, i.e. takes
|
||||
care of selecting the correct subclass.
|
||||
|
||||
@@ -103,11 +111,9 @@ class TransactionPartner(TelegramObject):
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if (cls is TransactionPartner and not data) or data is None:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[TransactionPartner]] = {
|
||||
cls.AFFILIATE_PROGRAM: TransactionPartnerAffiliateProgram,
|
||||
cls.CHAT: TransactionPartnerChat,
|
||||
cls.FRAGMENT: TransactionPartnerFragment,
|
||||
cls.USER: TransactionPartnerUser,
|
||||
cls.TELEGRAM_ADS: TransactionPartnerTelegramAds,
|
||||
@@ -166,15 +172,66 @@ class TransactionPartnerAffiliateProgram(TransactionPartner):
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["TransactionPartnerAffiliateProgram"]:
|
||||
cls, data: JSONDict, bot: Optional["Bot"] = None
|
||||
) -> "TransactionPartnerAffiliateProgram":
|
||||
"""See :meth:`telegram.TransactionPartner.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
data["sponsor_user"] = de_json_optional(data.get("sponsor_user"), User, bot)
|
||||
|
||||
data["sponsor_user"] = User.de_json(data.get("sponsor_user"), bot)
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
class TransactionPartnerChat(TransactionPartner):
|
||||
"""Describes a transaction with a chat.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`chat` are equal.
|
||||
|
||||
.. versionadded:: 21.11
|
||||
|
||||
Args:
|
||||
chat (:class:`telegram.Chat`): Information about the chat.
|
||||
gift (:class:`telegram.Gift`, optional): The gift sent to the chat by the bot.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The type of the transaction partner,
|
||||
always :tg-const:`telegram.TransactionPartner.CHAT`.
|
||||
chat (:class:`telegram.Chat`): Information about the chat.
|
||||
gift (:class:`telegram.Gift`): Optional. The gift sent to the user by the bot.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"chat",
|
||||
"gift",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
chat: Chat,
|
||||
gift: Optional[Gift] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> None:
|
||||
super().__init__(type=TransactionPartner.CHAT, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.chat: Chat = chat
|
||||
self.gift: Optional[Gift] = gift
|
||||
|
||||
self._id_attrs = (
|
||||
self.type,
|
||||
self.chat,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "TransactionPartnerChat":
|
||||
"""See :meth:`telegram.TransactionPartner.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
data["chat"] = de_json_optional(data.get("chat"), Chat, bot)
|
||||
data["gift"] = de_json_optional(data.get("gift"), Gift, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
@@ -209,17 +266,12 @@ class TransactionPartnerFragment(TransactionPartner):
|
||||
self.withdrawal_state: Optional[RevenueWithdrawalState] = withdrawal_state
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["TransactionPartnerFragment"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "TransactionPartnerFragment":
|
||||
"""See :meth:`telegram.TransactionPartner.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
data["withdrawal_state"] = RevenueWithdrawalState.de_json(
|
||||
data.get("withdrawal_state"), bot
|
||||
data["withdrawal_state"] = de_json_optional(
|
||||
data.get("withdrawal_state"), RevenueWithdrawalState, bot
|
||||
)
|
||||
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
@@ -320,24 +372,19 @@ class TransactionPartnerUser(TransactionPartner):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["TransactionPartnerUser"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "TransactionPartnerUser":
|
||||
"""See :meth:`telegram.TransactionPartner.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["user"] = User.de_json(data.get("user"), bot)
|
||||
data["affiliate"] = AffiliateInfo.de_json(data.get("affiliate"), bot)
|
||||
data["paid_media"] = PaidMedia.de_list(data.get("paid_media"), bot=bot)
|
||||
data["user"] = de_json_optional(data.get("user"), User, bot)
|
||||
data["affiliate"] = de_json_optional(data.get("affiliate"), AffiliateInfo, bot)
|
||||
data["paid_media"] = de_list_optional(data.get("paid_media"), PaidMedia, bot)
|
||||
data["subscription_period"] = (
|
||||
dtm.timedelta(seconds=sp)
|
||||
if (sp := data.get("subscription_period")) is not None
|
||||
else None
|
||||
)
|
||||
data["gift"] = Gift.de_json(data.get("gift"), bot)
|
||||
data["gift"] = de_json_optional(data.get("gift"), Gift, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._payment.orderinfo import OrderInfo
|
||||
from telegram._telegramobject import TelegramObject
|
||||
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
|
||||
|
||||
@@ -32,6 +33,9 @@ if TYPE_CHECKING:
|
||||
|
||||
class SuccessfulPayment(TelegramObject):
|
||||
"""This object contains basic information about a successful payment.
|
||||
Note that if the buyer initiates a chargeback with the relevant payment provider following
|
||||
this transaction, the funds may be debited from your balance. This is outside of
|
||||
Telegram's control.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`telegram_payment_charge_id` and
|
||||
@@ -138,16 +142,11 @@ class SuccessfulPayment(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["SuccessfulPayment"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "SuccessfulPayment":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["order_info"] = OrderInfo.de_json(data.get("order_info"), bot)
|
||||
data["order_info"] = de_json_optional(data.get("order_info"), OrderInfo, bot)
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
|
||||
+16
-30
@@ -27,7 +27,7 @@ from telegram._messageentity import MessageEntity
|
||||
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.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.entities import parse_message_entities, parse_message_entity
|
||||
@@ -91,16 +91,11 @@ class InputPollOption(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["InputPollOption"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "InputPollOption":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["text_entities"] = MessageEntity.de_list(data.get("text_entities"), bot)
|
||||
data["text_entities"] = de_list_optional(data.get("text_entities"), MessageEntity, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -157,16 +152,11 @@ class PollOption(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PollOption"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "PollOption":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["text_entities"] = MessageEntity.de_list(data.get("text_entities"), bot)
|
||||
data["text_entities"] = de_list_optional(data.get("text_entities"), MessageEntity, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -306,17 +296,12 @@ class PollAnswer(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["PollAnswer"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "PollAnswer":
|
||||
"""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["voter_chat"] = Chat.de_json(data.get("voter_chat"), bot)
|
||||
data["user"] = de_json_optional(data.get("user"), User, bot)
|
||||
data["voter_chat"] = de_json_optional(data.get("voter_chat"), Chat, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -474,20 +459,21 @@ class Poll(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["Poll"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Poll":
|
||||
"""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["options"] = [PollOption.de_json(option, bot) for option in data["options"]]
|
||||
data["explanation_entities"] = MessageEntity.de_list(data.get("explanation_entities"), bot)
|
||||
data["options"] = de_list_optional(data.get("options"), PollOption, bot)
|
||||
data["explanation_entities"] = de_list_optional(
|
||||
data.get("explanation_entities"), MessageEntity, bot
|
||||
)
|
||||
data["close_date"] = from_timestamp(data.get("close_date"), tzinfo=loc_tzinfo)
|
||||
data["question_entities"] = MessageEntity.de_list(data.get("question_entities"), bot)
|
||||
data["question_entities"] = de_list_optional(
|
||||
data.get("question_entities"), MessageEntity, bot
|
||||
)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -21,6 +21,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:
|
||||
@@ -67,16 +68,11 @@ class ProximityAlertTriggered(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ProximityAlertTriggered"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ProximityAlertTriggered":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["traveler"] = User.de_json(data.get("traveler"), bot)
|
||||
data["watcher"] = User.de_json(data.get("watcher"), bot)
|
||||
data["traveler"] = de_json_optional(data.get("traveler"), User, bot)
|
||||
data["watcher"] = de_json_optional(data.get("watcher"), User, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
+4
-16
@@ -23,6 +23,7 @@ from typing import TYPE_CHECKING, Final, Literal, Optional, Union
|
||||
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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -77,18 +78,10 @@ class ReactionType(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ReactionType"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ReactionType":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
if not data and cls is ReactionType:
|
||||
return None
|
||||
|
||||
_class_mapping: dict[str, type[ReactionType]] = {
|
||||
cls.EMOJI: ReactionTypeEmoji,
|
||||
cls.CUSTOM_EMOJI: ReactionTypeCustomEmoji,
|
||||
@@ -230,15 +223,10 @@ class ReactionCount(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ReactionCount"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ReactionCount":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["type"] = ReactionType.de_json(data.get("type"), bot)
|
||||
data["type"] = de_json_optional(data.get("type"), ReactionType, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
+33
-44
@@ -43,7 +43,7 @@ from telegram._payment.invoice import Invoice
|
||||
from telegram._poll import Poll
|
||||
from telegram._story import Story
|
||||
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.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
|
||||
@@ -248,39 +248,36 @@ class ExternalReplyInfo(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ExternalReplyInfo"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ExternalReplyInfo":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
data["origin"] = MessageOrigin.de_json(data.get("origin"), bot)
|
||||
data["chat"] = Chat.de_json(data.get("chat"), bot)
|
||||
data["link_preview_options"] = LinkPreviewOptions.de_json(
|
||||
data.get("link_preview_options"), bot
|
||||
data["origin"] = de_json_optional(data.get("origin"), MessageOrigin, bot)
|
||||
data["chat"] = de_json_optional(data.get("chat"), Chat, bot)
|
||||
data["link_preview_options"] = de_json_optional(
|
||||
data.get("link_preview_options"), LinkPreviewOptions, bot
|
||||
)
|
||||
data["animation"] = Animation.de_json(data.get("animation"), bot)
|
||||
data["audio"] = Audio.de_json(data.get("audio"), bot)
|
||||
data["document"] = Document.de_json(data.get("document"), bot)
|
||||
data["photo"] = tuple(PhotoSize.de_list(data.get("photo"), bot))
|
||||
data["sticker"] = Sticker.de_json(data.get("sticker"), bot)
|
||||
data["story"] = Story.de_json(data.get("story"), bot)
|
||||
data["video"] = Video.de_json(data.get("video"), bot)
|
||||
data["video_note"] = VideoNote.de_json(data.get("video_note"), bot)
|
||||
data["voice"] = Voice.de_json(data.get("voice"), bot)
|
||||
data["contact"] = Contact.de_json(data.get("contact"), bot)
|
||||
data["dice"] = Dice.de_json(data.get("dice"), bot)
|
||||
data["game"] = Game.de_json(data.get("game"), bot)
|
||||
data["giveaway"] = Giveaway.de_json(data.get("giveaway"), bot)
|
||||
data["giveaway_winners"] = GiveawayWinners.de_json(data.get("giveaway_winners"), bot)
|
||||
data["invoice"] = Invoice.de_json(data.get("invoice"), bot)
|
||||
data["location"] = Location.de_json(data.get("location"), bot)
|
||||
data["poll"] = Poll.de_json(data.get("poll"), bot)
|
||||
data["venue"] = Venue.de_json(data.get("venue"), bot)
|
||||
data["paid_media"] = PaidMediaInfo.de_json(data.get("paid_media"), bot)
|
||||
data["animation"] = de_json_optional(data.get("animation"), Animation, bot)
|
||||
data["audio"] = de_json_optional(data.get("audio"), Audio, bot)
|
||||
data["document"] = de_json_optional(data.get("document"), Document, bot)
|
||||
data["photo"] = de_list_optional(data.get("photo"), PhotoSize, bot)
|
||||
data["sticker"] = de_json_optional(data.get("sticker"), Sticker, bot)
|
||||
data["story"] = de_json_optional(data.get("story"), Story, bot)
|
||||
data["video"] = de_json_optional(data.get("video"), Video, bot)
|
||||
data["video_note"] = de_json_optional(data.get("video_note"), VideoNote, bot)
|
||||
data["voice"] = de_json_optional(data.get("voice"), Voice, bot)
|
||||
data["contact"] = de_json_optional(data.get("contact"), Contact, bot)
|
||||
data["dice"] = de_json_optional(data.get("dice"), Dice, bot)
|
||||
data["game"] = de_json_optional(data.get("game"), Game, bot)
|
||||
data["giveaway"] = de_json_optional(data.get("giveaway"), Giveaway, bot)
|
||||
data["giveaway_winners"] = de_json_optional(
|
||||
data.get("giveaway_winners"), GiveawayWinners, bot
|
||||
)
|
||||
data["invoice"] = de_json_optional(data.get("invoice"), Invoice, bot)
|
||||
data["location"] = de_json_optional(data.get("location"), Location, bot)
|
||||
data["poll"] = de_json_optional(data.get("poll"), Poll, bot)
|
||||
data["venue"] = de_json_optional(data.get("venue"), Venue, bot)
|
||||
data["paid_media"] = de_json_optional(data.get("paid_media"), PaidMediaInfo, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -350,16 +347,11 @@ class TextQuote(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["TextQuote"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "TextQuote":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
data["entities"] = tuple(MessageEntity.de_list(data.get("entities"), bot))
|
||||
data["entities"] = de_list_optional(data.get("entities"), MessageEntity, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -458,15 +450,12 @@ class ReplyParameters(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ReplyParameters"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ReplyParameters":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
data["quote_entities"] = tuple(MessageEntity.de_list(data.get("quote_entities"), bot))
|
||||
data["quote_entities"] = tuple(
|
||||
de_list_optional(data.get("quote_entities"), MessageEntity, bot)
|
||||
)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
+7
-22
@@ -22,7 +22,7 @@ from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._files.photosize import PhotoSize
|
||||
from telegram._telegramobject import TelegramObject
|
||||
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:
|
||||
@@ -84,16 +84,11 @@ class UsersShared(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["UsersShared"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "UsersShared":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["users"] = SharedUser.de_list(data.get("users"), bot)
|
||||
data["users"] = de_list_optional(data.get("users"), SharedUser, bot)
|
||||
|
||||
api_kwargs = {}
|
||||
# This is a deprecated field that TG still returns for backwards compatibility
|
||||
@@ -175,16 +170,11 @@ class ChatShared(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["ChatShared"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "ChatShared":
|
||||
"""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["photo"] = de_list_optional(data.get("photo"), PhotoSize, bot)
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
@@ -255,14 +245,9 @@ class SharedUser(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["SharedUser"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "SharedUser":
|
||||
"""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["photo"] = de_list_optional(data.get("photo"), PhotoSize, bot)
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
+1
-4
@@ -71,12 +71,9 @@ class Story(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["Story"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Story":
|
||||
"""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)
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -377,23 +377,20 @@ class TelegramObject:
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _parse_data(data: Optional[JSONDict]) -> Optional[JSONDict]:
|
||||
def _parse_data(data: JSONDict) -> JSONDict:
|
||||
"""Should be called by subclasses that override de_json to ensure that the input
|
||||
is not altered. Whoever calls de_json might still want to use the original input
|
||||
for something else.
|
||||
"""
|
||||
return None if data is None else data.copy()
|
||||
return data.copy()
|
||||
|
||||
@classmethod
|
||||
def _de_json(
|
||||
cls: type[Tele_co],
|
||||
data: Optional[JSONDict],
|
||||
data: JSONDict,
|
||||
bot: Optional["Bot"],
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> Optional[Tele_co]:
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
) -> Tele_co:
|
||||
# try-except is significantly faster in case we already have a correct argument set
|
||||
try:
|
||||
obj = cls(**data, api_kwargs=api_kwargs)
|
||||
@@ -417,9 +414,7 @@ class TelegramObject:
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls: type[Tele_co], data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional[Tele_co]:
|
||||
def de_json(cls: type[Tele_co], data: JSONDict, bot: Optional["Bot"] = None) -> Tele_co:
|
||||
"""Converts JSON data to a Telegram object.
|
||||
|
||||
Args:
|
||||
@@ -438,7 +433,7 @@ class TelegramObject:
|
||||
|
||||
@classmethod
|
||||
def de_list(
|
||||
cls: type[Tele_co], data: Optional[list[JSONDict]], bot: Optional["Bot"] = None
|
||||
cls: type[Tele_co], data: list[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> tuple[Tele_co, ...]:
|
||||
"""Converts a list of JSON objects to a tuple of Telegram objects.
|
||||
|
||||
@@ -459,10 +454,7 @@ class TelegramObject:
|
||||
A tuple of Telegram objects.
|
||||
|
||||
"""
|
||||
if not data:
|
||||
return ()
|
||||
|
||||
return tuple(obj for obj in (cls.de_json(d, bot) for d in data) if obj is not None)
|
||||
return tuple(cls.de_json(d, bot) for d in data)
|
||||
|
||||
@contextmanager
|
||||
def _unfrozen(self: Tele_co) -> Iterator[Tele_co]:
|
||||
|
||||
+43
-33
@@ -35,6 +35,7 @@ from telegram._payment.precheckoutquery import PreCheckoutQuery
|
||||
from telegram._payment.shippingquery import ShippingQuery
|
||||
from telegram._poll import Poll, PollAnswer
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import de_json_optional
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._utils.warnings import warn
|
||||
|
||||
@@ -757,47 +758,56 @@ class Update(TelegramObject):
|
||||
return message
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["Update"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Update":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["message"] = Message.de_json(data.get("message"), bot)
|
||||
data["edited_message"] = Message.de_json(data.get("edited_message"), bot)
|
||||
data["inline_query"] = InlineQuery.de_json(data.get("inline_query"), bot)
|
||||
data["chosen_inline_result"] = ChosenInlineResult.de_json(
|
||||
data.get("chosen_inline_result"), bot
|
||||
data["message"] = de_json_optional(data.get("message"), Message, bot)
|
||||
data["edited_message"] = de_json_optional(data.get("edited_message"), Message, bot)
|
||||
data["inline_query"] = de_json_optional(data.get("inline_query"), InlineQuery, bot)
|
||||
data["chosen_inline_result"] = de_json_optional(
|
||||
data.get("chosen_inline_result"), ChosenInlineResult, bot
|
||||
)
|
||||
data["callback_query"] = CallbackQuery.de_json(data.get("callback_query"), bot)
|
||||
data["shipping_query"] = ShippingQuery.de_json(data.get("shipping_query"), bot)
|
||||
data["pre_checkout_query"] = PreCheckoutQuery.de_json(data.get("pre_checkout_query"), bot)
|
||||
data["channel_post"] = Message.de_json(data.get("channel_post"), bot)
|
||||
data["edited_channel_post"] = Message.de_json(data.get("edited_channel_post"), bot)
|
||||
data["poll"] = Poll.de_json(data.get("poll"), bot)
|
||||
data["poll_answer"] = PollAnswer.de_json(data.get("poll_answer"), bot)
|
||||
data["my_chat_member"] = ChatMemberUpdated.de_json(data.get("my_chat_member"), bot)
|
||||
data["chat_member"] = ChatMemberUpdated.de_json(data.get("chat_member"), bot)
|
||||
data["chat_join_request"] = ChatJoinRequest.de_json(data.get("chat_join_request"), bot)
|
||||
data["chat_boost"] = ChatBoostUpdated.de_json(data.get("chat_boost"), bot)
|
||||
data["removed_chat_boost"] = ChatBoostRemoved.de_json(data.get("removed_chat_boost"), bot)
|
||||
data["message_reaction"] = MessageReactionUpdated.de_json(
|
||||
data.get("message_reaction"), bot
|
||||
data["callback_query"] = de_json_optional(data.get("callback_query"), CallbackQuery, bot)
|
||||
data["shipping_query"] = de_json_optional(data.get("shipping_query"), ShippingQuery, bot)
|
||||
data["pre_checkout_query"] = de_json_optional(
|
||||
data.get("pre_checkout_query"), PreCheckoutQuery, bot
|
||||
)
|
||||
data["message_reaction_count"] = MessageReactionCountUpdated.de_json(
|
||||
data.get("message_reaction_count"), bot
|
||||
data["channel_post"] = de_json_optional(data.get("channel_post"), Message, bot)
|
||||
data["edited_channel_post"] = de_json_optional(
|
||||
data.get("edited_channel_post"), Message, bot
|
||||
)
|
||||
data["business_connection"] = BusinessConnection.de_json(
|
||||
data.get("business_connection"), bot
|
||||
data["poll"] = de_json_optional(data.get("poll"), Poll, bot)
|
||||
data["poll_answer"] = de_json_optional(data.get("poll_answer"), PollAnswer, bot)
|
||||
data["my_chat_member"] = de_json_optional(
|
||||
data.get("my_chat_member"), ChatMemberUpdated, bot
|
||||
)
|
||||
data["business_message"] = Message.de_json(data.get("business_message"), bot)
|
||||
data["edited_business_message"] = Message.de_json(data.get("edited_business_message"), bot)
|
||||
data["deleted_business_messages"] = BusinessMessagesDeleted.de_json(
|
||||
data.get("deleted_business_messages"), bot
|
||||
data["chat_member"] = de_json_optional(data.get("chat_member"), ChatMemberUpdated, bot)
|
||||
data["chat_join_request"] = de_json_optional(
|
||||
data.get("chat_join_request"), ChatJoinRequest, bot
|
||||
)
|
||||
data["purchased_paid_media"] = PaidMediaPurchased.de_json(
|
||||
data.get("purchased_paid_media"), bot
|
||||
data["chat_boost"] = de_json_optional(data.get("chat_boost"), ChatBoostUpdated, bot)
|
||||
data["removed_chat_boost"] = de_json_optional(
|
||||
data.get("removed_chat_boost"), ChatBoostRemoved, bot
|
||||
)
|
||||
data["message_reaction"] = de_json_optional(
|
||||
data.get("message_reaction"), MessageReactionUpdated, bot
|
||||
)
|
||||
data["message_reaction_count"] = de_json_optional(
|
||||
data.get("message_reaction_count"), MessageReactionCountUpdated, bot
|
||||
)
|
||||
data["business_connection"] = de_json_optional(
|
||||
data.get("business_connection"), BusinessConnection, bot
|
||||
)
|
||||
data["business_message"] = de_json_optional(data.get("business_message"), Message, bot)
|
||||
data["edited_business_message"] = de_json_optional(
|
||||
data.get("edited_business_message"), Message, bot
|
||||
)
|
||||
data["deleted_business_messages"] = de_json_optional(
|
||||
data.get("deleted_business_messages"), BusinessMessagesDeleted, bot
|
||||
)
|
||||
data["purchased_paid_media"] = de_json_optional(
|
||||
data.get("purchased_paid_media"), PaidMediaPurchased, bot
|
||||
)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
+30
-10
@@ -26,7 +26,14 @@ from telegram._inline.inlinekeyboardbutton import InlineKeyboardButton
|
||||
from telegram._menubutton import MenuButton
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import CorrectOptionID, FileInput, JSONDict, ODVInput, ReplyMarkup
|
||||
from telegram._utils.types import (
|
||||
CorrectOptionID,
|
||||
FileInput,
|
||||
JSONDict,
|
||||
ODVInput,
|
||||
ReplyMarkup,
|
||||
TimePeriod,
|
||||
)
|
||||
from telegram.helpers import mention_html as helpers_mention_html
|
||||
from telegram.helpers import mention_markdown as helpers_mention_markdown
|
||||
|
||||
@@ -668,7 +675,7 @@ class User(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,
|
||||
@@ -1011,9 +1018,9 @@ class User(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,
|
||||
@@ -1113,7 +1120,7 @@ class User(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,
|
||||
@@ -1175,7 +1182,7 @@ class User(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,
|
||||
@@ -1303,7 +1310,7 @@ class User(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,
|
||||
@@ -1321,6 +1328,8 @@ class User(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,
|
||||
@@ -1362,6 +1371,8 @@ class User(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,
|
||||
@@ -1447,7 +1458,7 @@ class User(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,
|
||||
@@ -1508,7 +1519,7 @@ class User(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,
|
||||
@@ -1581,7 +1592,7 @@ class User(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,
|
||||
@@ -1663,7 +1674,7 @@ class User(TelegramObject):
|
||||
) -> bool:
|
||||
"""Shortcut for::
|
||||
|
||||
await bot.send_gift( user_id=update.effective_user.id, *args, **kwargs )
|
||||
await bot.send_gift(user_id=update.effective_user.id, *args, **kwargs )
|
||||
|
||||
For the documentation of the arguments, please see :meth:`telegram.Bot.send_gift`.
|
||||
|
||||
@@ -1673,6 +1684,7 @@ class User(TelegramObject):
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
"""
|
||||
return await self.get_bot().send_gift(
|
||||
chat_id=None,
|
||||
user_id=self.id,
|
||||
gift_id=gift_id,
|
||||
text=text,
|
||||
@@ -1700,6 +1712,7 @@ class User(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,
|
||||
@@ -1727,6 +1740,7 @@ class User(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,
|
||||
@@ -1759,6 +1773,7 @@ class User(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,
|
||||
@@ -1786,6 +1801,7 @@ class User(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,
|
||||
@@ -1901,6 +1917,7 @@ class User(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,
|
||||
@@ -1926,6 +1943,7 @@ class User(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,
|
||||
@@ -1943,6 +1961,7 @@ class User(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,
|
||||
@@ -1969,6 +1988,7 @@ class User(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,
|
||||
|
||||
@@ -71,15 +71,10 @@ class UserProfilePhotos(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["UserProfilePhotos"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "UserProfilePhotos":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["photos"] = [PhotoSize.de_list(photo, bot) for photo in data["photos"]]
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -24,10 +24,16 @@ Warning:
|
||||
the changelog.
|
||||
"""
|
||||
from collections.abc import Sequence
|
||||
from typing import Optional, TypeVar
|
||||
from typing import TYPE_CHECKING, Optional, Protocol, TypeVar
|
||||
|
||||
from telegram._linkpreviewoptions import LinkPreviewOptions
|
||||
from telegram._utils.types import ODVInput
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import type_check_only
|
||||
|
||||
from telegram import Bot, FileCredentials
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
@@ -60,3 +66,75 @@ def parse_lpo_and_dwpp(
|
||||
link_preview_options = LinkPreviewOptions(is_disabled=disable_web_page_preview)
|
||||
|
||||
return link_preview_options
|
||||
|
||||
|
||||
Tele_co = TypeVar("Tele_co", bound=TelegramObject, covariant=True)
|
||||
TeleCrypto_co = TypeVar("TeleCrypto_co", bound="HasDecryptMethod", covariant=True)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@type_check_only
|
||||
class HasDecryptMethod(Protocol):
|
||||
__slots__ = ()
|
||||
|
||||
@classmethod
|
||||
def de_json_decrypted(
|
||||
cls: type[TeleCrypto_co],
|
||||
data: JSONDict,
|
||||
bot: Optional["Bot"],
|
||||
credentials: list["FileCredentials"],
|
||||
) -> TeleCrypto_co: ...
|
||||
|
||||
@classmethod
|
||||
def de_list_decrypted(
|
||||
cls: type[TeleCrypto_co],
|
||||
data: list[JSONDict],
|
||||
bot: Optional["Bot"],
|
||||
credentials: list["FileCredentials"],
|
||||
) -> tuple[TeleCrypto_co, ...]: ...
|
||||
|
||||
|
||||
def de_json_optional(
|
||||
data: Optional[JSONDict], cls: type[Tele_co], bot: Optional["Bot"]
|
||||
) -> Optional[Tele_co]:
|
||||
"""Wrapper around TO.de_json that returns None if data is None."""
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
return cls.de_json(data, bot)
|
||||
|
||||
|
||||
def de_json_decrypted_optional(
|
||||
data: Optional[JSONDict],
|
||||
cls: type[TeleCrypto_co],
|
||||
bot: Optional["Bot"],
|
||||
credentials: list["FileCredentials"],
|
||||
) -> Optional[TeleCrypto_co]:
|
||||
"""Wrapper around TO.de_json_decrypted that returns None if data is None."""
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
return cls.de_json_decrypted(data, bot, credentials)
|
||||
|
||||
|
||||
def de_list_optional(
|
||||
data: Optional[list[JSONDict]], cls: type[Tele_co], bot: Optional["Bot"]
|
||||
) -> tuple[Tele_co, ...]:
|
||||
"""Wrapper around TO.de_list that returns an empty list if data is None."""
|
||||
if data is None:
|
||||
return ()
|
||||
|
||||
return cls.de_list(data, bot)
|
||||
|
||||
|
||||
def de_list_decrypted_optional(
|
||||
data: Optional[list[JSONDict]],
|
||||
cls: type[TeleCrypto_co],
|
||||
bot: Optional["Bot"],
|
||||
credentials: list["FileCredentials"],
|
||||
) -> tuple[TeleCrypto_co, ...]:
|
||||
"""Wrapper around TO.de_list_decrypted that returns an empty list if data is None."""
|
||||
if data is None:
|
||||
return ()
|
||||
|
||||
return cls.de_list_decrypted(data, bot, credentials)
|
||||
|
||||
@@ -23,9 +23,10 @@ Warning:
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
import datetime as dtm
|
||||
from collections.abc import Collection
|
||||
from pathlib import Path
|
||||
from typing import IO, TYPE_CHECKING, Any, Literal, Optional, TypeVar, Union
|
||||
from typing import IO, TYPE_CHECKING, Any, Callable, Literal, Optional, TypeVar, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import (
|
||||
@@ -91,3 +92,7 @@ SocketOpt = Union[
|
||||
tuple[int, int, Union[bytes, bytearray]],
|
||||
tuple[int, int, None, int],
|
||||
]
|
||||
|
||||
BaseUrl = Union[str, Callable[[str], str]]
|
||||
|
||||
TimePeriod = Union[int, dtm.timedelta]
|
||||
|
||||
@@ -51,6 +51,6 @@ class Version(NamedTuple):
|
||||
|
||||
|
||||
__version_info__: Final[Version] = Version(
|
||||
major=21, minor=10, micro=0, releaselevel="final", serial=0
|
||||
major=21, minor=11, micro=1, releaselevel="final", serial=0
|
||||
)
|
||||
__version__: Final[str] = str(__version_info__)
|
||||
|
||||
+4
-12
@@ -126,14 +126,11 @@ class VideoChatParticipantsInvited(TelegramObject):
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["VideoChatParticipantsInvited"]:
|
||||
cls, data: JSONDict, bot: Optional["Bot"] = None
|
||||
) -> "VideoChatParticipantsInvited":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["users"] = User.de_list(data.get("users", []), bot)
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -178,18 +175,13 @@ class VideoChatScheduled(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["VideoChatScheduled"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "VideoChatScheduled":
|
||||
"""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["start_date"] = from_timestamp(data["start_date"], tzinfo=loc_tzinfo)
|
||||
data["start_date"] = from_timestamp(data.get("start_date"), tzinfo=loc_tzinfo)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@@ -166,15 +166,10 @@ class WebhookInfo(TelegramObject):
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
|
||||
) -> Optional["WebhookInfo"]:
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "WebhookInfo":
|
||||
"""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)
|
||||
|
||||
|
||||
+13
-5
@@ -155,7 +155,7 @@ class _AccentColor(NamedTuple):
|
||||
#: :data:`telegram.__bot_api_version_info__`.
|
||||
#:
|
||||
#: .. versionadded:: 20.0
|
||||
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=8, minor=2)
|
||||
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=8, minor=3)
|
||||
#: :obj:`str`: Telegram Bot API
|
||||
#: version supported by this version of `python-telegram-bot`. Also available as
|
||||
#: :data:`telegram.__bot_api_version__`.
|
||||
@@ -1236,9 +1236,12 @@ class GiftLimit(IntEnum):
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
MAX_TEXT_LENGTH = 255
|
||||
MAX_TEXT_LENGTH = 128
|
||||
""":obj:`int`: Maximum number of characters in a :obj:`str` passed as the
|
||||
:paramref:`~telegram.Bot.send_gift.text` parameter of :meth:`~telegram.Bot.send_gift`.
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
Updated Value to 128 based on Bot API 8.3
|
||||
"""
|
||||
|
||||
|
||||
@@ -2618,13 +2621,13 @@ class StickerSetLimit(IntEnum):
|
||||
:meth:`telegram.Bot.add_sticker_to_set`.
|
||||
"""
|
||||
MAX_STATIC_THUMBNAIL_SIZE = 128
|
||||
""":obj:`int`: Maximum size of the thumbnail if it is a **.WEBP** or **.PNG** in kilobytes,
|
||||
""":obj:`int`: Maximum size of the thumbnail if it is a ``.WEBP`` or ``.PNG`` in kilobytes,
|
||||
as given in :meth:`telegram.Bot.set_sticker_set_thumbnail`."""
|
||||
MAX_ANIMATED_THUMBNAIL_SIZE = 32
|
||||
""":obj:`int`: Maximum size of the thumbnail if it is a **.TGS** or **.WEBM** in kilobytes,
|
||||
""":obj:`int`: Maximum size of the thumbnail if it is a ``.TGS`` or ``.WEBM`` in kilobytes,
|
||||
as given in :meth:`telegram.Bot.set_sticker_set_thumbnail`."""
|
||||
STATIC_THUMB_DIMENSIONS = 100
|
||||
""":obj:`int`: Exact height and width of the thumbnail if it is a **.WEBP** or **.PNG** in
|
||||
""":obj:`int`: Exact height and width of the thumbnail if it is a ``.WEBP`` or ``.PNG`` in
|
||||
pixels, as given in :meth:`telegram.Bot.set_sticker_set_thumbnail`."""
|
||||
|
||||
|
||||
@@ -2659,6 +2662,11 @@ class TransactionPartnerType(StringEnum):
|
||||
|
||||
.. versionadded:: 21.9
|
||||
"""
|
||||
CHAT = "chat"
|
||||
""":obj:`str`: Transaction with a chat.
|
||||
|
||||
.. versionadded:: 21.11
|
||||
"""
|
||||
FRAGMENT = "fragment"
|
||||
""":obj:`str`: Withdrawal transaction with Fragment."""
|
||||
OTHER = "other"
|
||||
|
||||
@@ -32,6 +32,7 @@ try:
|
||||
except ImportError:
|
||||
AIO_LIMITER_AVAILABLE = False
|
||||
|
||||
from telegram import constants
|
||||
from telegram._utils.logging import get_logger
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram.error import RetryAfter
|
||||
@@ -86,7 +87,8 @@ class AIORateLimiter(BaseRateLimiter[int]):
|
||||
* A :exc:`~telegram.error.RetryAfter` exception will halt *all* requests for
|
||||
:attr:`~telegram.error.RetryAfter.retry_after` + 0.1 seconds. This may be stricter than
|
||||
necessary in some cases, e.g. the bot may hit a rate limit in one group but might still
|
||||
be allowed to send messages in another group.
|
||||
be allowed to send messages in another group or with
|
||||
:paramref:`~telegram.Bot.send_message.allow_paid_broadcast` set to :obj:`True`.
|
||||
|
||||
Tip:
|
||||
With `Bot API 7.1 <https://core.telegram.org/bots/api-changelog#october-31-2024>`_
|
||||
@@ -96,10 +98,10 @@ class AIORateLimiter(BaseRateLimiter[int]):
|
||||
:tg-const:`telegram.constants.FloodLimit.PAID_MESSAGES_PER_SECOND` messages per second by
|
||||
paying a fee in Telegram Stars.
|
||||
|
||||
.. caution::
|
||||
This class currently doesn't take the
|
||||
:paramref:`~telegram.Bot.send_message.allow_paid_broadcast` parameter into account.
|
||||
This means that the rate limiting is applied just like for any other message.
|
||||
.. versionchanged:: 21.11
|
||||
This class automatically takes the
|
||||
:paramref:`~telegram.Bot.send_message.allow_paid_broadcast` parameter into account and
|
||||
throttles the requests accordingly.
|
||||
|
||||
Note:
|
||||
This class is to be understood as minimal effort reference implementation.
|
||||
@@ -114,16 +116,17 @@ class AIORateLimiter(BaseRateLimiter[int]):
|
||||
Args:
|
||||
overall_max_rate (:obj:`float`): The maximum number of requests allowed for the entire bot
|
||||
per :paramref:`overall_time_period`. When set to 0, no rate limiting will be applied.
|
||||
Defaults to ``30``.
|
||||
Defaults to :tg-const:`telegram.constants.FloodLimit.MESSAGES_PER_SECOND`.
|
||||
overall_time_period (:obj:`float`): The time period (in seconds) during which the
|
||||
:paramref:`overall_max_rate` is enforced. When set to 0, no rate limiting will be
|
||||
applied. Defaults to 1.
|
||||
applied. Defaults to ``1``.
|
||||
group_max_rate (:obj:`float`): The maximum number of requests allowed for requests related
|
||||
to groups and channels per :paramref:`group_time_period`. When set to 0, no rate
|
||||
limiting will be applied. Defaults to 20.
|
||||
limiting will be applied. Defaults to
|
||||
:tg-const:`telegram.constants.FloodLimit.MESSAGES_PER_MINUTE_PER_GROUP`.
|
||||
group_time_period (:obj:`float`): The time period (in seconds) during which the
|
||||
:paramref:`group_max_rate` is enforced. When set to 0, no rate limiting will be
|
||||
applied. Defaults to 60.
|
||||
applied. Defaults to ``60``.
|
||||
max_retries (:obj:`int`): The maximum number of retries to be made in case of a
|
||||
:exc:`~telegram.error.RetryAfter` exception.
|
||||
If set to 0, no retries will be made. Defaults to ``0``.
|
||||
@@ -131,6 +134,7 @@ class AIORateLimiter(BaseRateLimiter[int]):
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"_apb_limiter",
|
||||
"_base_limiter",
|
||||
"_group_limiters",
|
||||
"_group_max_rate",
|
||||
@@ -141,9 +145,9 @@ class AIORateLimiter(BaseRateLimiter[int]):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
overall_max_rate: float = 30,
|
||||
overall_max_rate: float = constants.FloodLimit.MESSAGES_PER_SECOND,
|
||||
overall_time_period: float = 1,
|
||||
group_max_rate: float = 20,
|
||||
group_max_rate: float = constants.FloodLimit.MESSAGES_PER_MINUTE_PER_GROUP,
|
||||
group_time_period: float = 60,
|
||||
max_retries: int = 0,
|
||||
) -> None:
|
||||
@@ -167,6 +171,9 @@ class AIORateLimiter(BaseRateLimiter[int]):
|
||||
self._group_time_period = 0
|
||||
|
||||
self._group_limiters: dict[Union[str, int], AsyncLimiter] = {}
|
||||
self._apb_limiter: AsyncLimiter = AsyncLimiter(
|
||||
max_rate=constants.FloodLimit.PAID_MESSAGES_PER_SECOND, time_period=1
|
||||
)
|
||||
self._max_retries: int = max_retries
|
||||
self._retry_after_event = asyncio.Event()
|
||||
self._retry_after_event.set()
|
||||
@@ -201,21 +208,30 @@ class AIORateLimiter(BaseRateLimiter[int]):
|
||||
self,
|
||||
chat: bool,
|
||||
group: Union[str, int, bool],
|
||||
allow_paid_broadcast: bool,
|
||||
callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, list[JSONDict]]]],
|
||||
args: Any,
|
||||
kwargs: dict[str, Any],
|
||||
) -> Union[bool, JSONDict, list[JSONDict]]:
|
||||
base_context = self._base_limiter if (chat and self._base_limiter) else null_context()
|
||||
group_context = (
|
||||
self._get_group_limiter(group) if group and self._group_max_rate else null_context()
|
||||
)
|
||||
|
||||
async with group_context, base_context:
|
||||
async def inner() -> Union[bool, JSONDict, list[JSONDict]]:
|
||||
# In case a retry_after was hit, we wait with processing the request
|
||||
await self._retry_after_event.wait()
|
||||
|
||||
return await callback(*args, **kwargs)
|
||||
|
||||
if allow_paid_broadcast:
|
||||
async with self._apb_limiter:
|
||||
return await inner()
|
||||
else:
|
||||
base_context = self._base_limiter if (chat and self._base_limiter) else null_context()
|
||||
group_context = (
|
||||
self._get_group_limiter(group)
|
||||
if group and self._group_max_rate
|
||||
else null_context()
|
||||
)
|
||||
|
||||
async with group_context, base_context:
|
||||
return await inner()
|
||||
|
||||
# mypy doesn't understand that the last run of the for loop raises an exception
|
||||
async def process_request(
|
||||
self,
|
||||
@@ -242,12 +258,13 @@ class AIORateLimiter(BaseRateLimiter[int]):
|
||||
group: Union[int, str, bool] = False
|
||||
chat: bool = False
|
||||
chat_id = data.get("chat_id")
|
||||
allow_paid_broadcast = data.get("allow_paid_broadcast", False)
|
||||
if chat_id is not None:
|
||||
chat = True
|
||||
|
||||
# In case user passes integer chat id as string
|
||||
with contextlib.suppress(ValueError, TypeError):
|
||||
chat_id = int(chat_id)
|
||||
chat_id = int(chat_id) # type: ignore[arg-type]
|
||||
|
||||
if (isinstance(chat_id, int) and chat_id < 0) or isinstance(chat_id, str):
|
||||
# string chat_id only works for channels and supergroups
|
||||
@@ -257,7 +274,12 @@ class AIORateLimiter(BaseRateLimiter[int]):
|
||||
for i in range(max_retries + 1):
|
||||
try:
|
||||
return await self._run_request(
|
||||
chat=chat, group=group, callback=callback, args=args, kwargs=kwargs
|
||||
chat=chat,
|
||||
group=group,
|
||||
allow_paid_broadcast=allow_paid_broadcast,
|
||||
callback=callback,
|
||||
args=args,
|
||||
kwargs=kwargs,
|
||||
)
|
||||
except RetryAfter as exc:
|
||||
if i == max_retries:
|
||||
|
||||
@@ -50,6 +50,7 @@ from telegram.ext._contexttypes import ContextTypes
|
||||
from telegram.ext._extbot import ExtBot
|
||||
from telegram.ext._handlers.basehandler import BaseHandler
|
||||
from telegram.ext._updater import Updater
|
||||
from telegram.ext._utils.networkloop import network_retry_loop
|
||||
from telegram.ext._utils.stack import was_called_by
|
||||
from telegram.ext._utils.trackingdict import TrackingDict
|
||||
from telegram.ext._utils.types import BD, BT, CCT, CD, JQ, RT, UD, ConversationKey, HandlerCallback
|
||||
@@ -235,7 +236,7 @@ class Application(
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
( # noqa: RUF005
|
||||
(
|
||||
"__create_task_tasks",
|
||||
"__update_fetcher_task",
|
||||
"__update_persistence_event",
|
||||
@@ -270,9 +271,7 @@ class Application(
|
||||
# Allowing '__weakref__' creation here since we need it for the JobQueue
|
||||
# Currently the __weakref__ slot is already created
|
||||
# in the AsyncContextManager base class for pythons < 3.13
|
||||
+ ("__weakref__",)
|
||||
if sys.version_info >= (3, 13)
|
||||
else ()
|
||||
+ (("__weakref__",) if sys.version_info >= (3, 13) else ())
|
||||
)
|
||||
|
||||
def __init__(
|
||||
@@ -741,7 +740,7 @@ class Application(
|
||||
self,
|
||||
poll_interval: float = 0.0,
|
||||
timeout: int = 10,
|
||||
bootstrap_retries: int = -1,
|
||||
bootstrap_retries: int = 0,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -782,13 +781,19 @@ class Application(
|
||||
Telegram in seconds. Default is ``0.0``.
|
||||
timeout (:obj:`int`, optional): Passed to
|
||||
:paramref:`telegram.Bot.get_updates.timeout`. Default is ``10`` seconds.
|
||||
bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase of the
|
||||
:class:`telegram.ext.Updater` will retry on failures on the Telegram server.
|
||||
bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase
|
||||
(calling :meth:`initialize` and the boostrapping of
|
||||
:meth:`telegram.ext.Updater.start_polling`)
|
||||
will retry on failures on the Telegram server.
|
||||
|
||||
* < 0 - retry indefinitely (default)
|
||||
* 0 - no retries
|
||||
* < 0 - retry indefinitely
|
||||
* 0 - no retries (default)
|
||||
* > 0 - retry up to X times
|
||||
|
||||
.. versionchanged:: 21.11
|
||||
The default value will be changed to from ``-1`` to ``0``. Indefinite retries
|
||||
during bootstrapping are not recommended.
|
||||
|
||||
read_timeout (:obj:`float`, optional): Value to pass to
|
||||
:paramref:`telegram.Bot.get_updates.read_timeout`. Defaults to
|
||||
:attr:`~telegram.request.BaseRequest.DEFAULT_NONE`.
|
||||
@@ -878,8 +883,9 @@ class Application(
|
||||
drop_pending_updates=drop_pending_updates,
|
||||
error_callback=error_callback, # if there is an error in fetching updates
|
||||
),
|
||||
close_loop=close_loop,
|
||||
stop_signals=stop_signals,
|
||||
bootstrap_retries=bootstrap_retries,
|
||||
close_loop=close_loop,
|
||||
)
|
||||
|
||||
def run_webhook(
|
||||
@@ -948,8 +954,10 @@ class Application(
|
||||
url_path (:obj:`str`, optional): Path inside url. Defaults to `` '' ``
|
||||
cert (:class:`pathlib.Path` | :obj:`str`, optional): Path to the SSL certificate file.
|
||||
key (:class:`pathlib.Path` | :obj:`str`, optional): Path to the SSL key file.
|
||||
bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase of the
|
||||
:class:`telegram.ext.Updater` will retry on failures on the Telegram server.
|
||||
bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase
|
||||
(calling :meth:`initialize` and the boostrapping of
|
||||
:meth:`telegram.ext.Updater.start_polling`)
|
||||
will retry on failures on the Telegram server.
|
||||
|
||||
* < 0 - retry indefinitely
|
||||
* 0 - no retries (default)
|
||||
@@ -1035,18 +1043,28 @@ class Application(
|
||||
secret_token=secret_token,
|
||||
unix=unix,
|
||||
),
|
||||
close_loop=close_loop,
|
||||
stop_signals=stop_signals,
|
||||
bootstrap_retries=bootstrap_retries,
|
||||
close_loop=close_loop,
|
||||
)
|
||||
|
||||
async def _bootstrap_initialize(self, max_retries: int) -> None:
|
||||
await network_retry_loop(
|
||||
action_cb=self.initialize,
|
||||
description="Bootstrap Initialize Application",
|
||||
max_retries=max_retries,
|
||||
interval=1,
|
||||
)
|
||||
|
||||
def __run(
|
||||
self,
|
||||
updater_coroutine: Coroutine,
|
||||
stop_signals: ODVInput[Sequence[int]],
|
||||
bootstrap_retries: int,
|
||||
close_loop: bool = True,
|
||||
) -> None:
|
||||
# Calling get_event_loop() should still be okay even in py3.10+ as long as there is a
|
||||
# running event loop or we are in the main thread, which are the intended use cases.
|
||||
# running event loop, or we are in the main thread, which are the intended use cases.
|
||||
# See the docs of get_event_loop() and get_running_loop() for more info
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
@@ -1066,7 +1084,7 @@ class Application(
|
||||
)
|
||||
|
||||
try:
|
||||
loop.run_until_complete(self.initialize())
|
||||
loop.run_until_complete(self._bootstrap_initialize(max_retries=bootstrap_retries))
|
||||
if self.post_init:
|
||||
loop.run_until_complete(self.post_init(self))
|
||||
if self.__stop_running_marker.is_set():
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user