diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000000000000000000000000000000000..8a8399b2db2a78d9b0ffcdc30b0dde386c2ab9c2 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,7 @@ +# .git-blame-ignore-revs +# Use locally as `git blame file.py --ignore-revs-file .git-blame-ignore-revs` +# or configure git to always use it: `git config blame.ignoreRevsFile .git-blame-ignore-revs` +# First migration to code style Black (#2122) +264b2c9c72691c5937b80e84e061c52dd2d8861a +# Use Black more extensively (#2972) +950d9a0751d79b92d78ea44344ce3e3c5b3948f9 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..470d2a2aac1658624a80b69013e876dda947fbec --- /dev/null +++ b/.gitignore @@ -0,0 +1,97 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg +.env +.pybuild +debian/tmp +debian/python3-telegram +debian/python3-telegram-doc +debian/.debhelper + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +.pytest_cache +.mypy_cache +nosetests.xml +coverage.xml +*,cover +.coveralls.yml +.testmondata +.testmondata-journal + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ +.idea/ + +# Sublime Text 2 +*.sublime* + +# VS Code +.vscode + +# unitests files +game.gif +telegram.mp3 +telegram.mp4 +telegram2.mp4 +telegram.ogg +telegram.png +telegram.webp +telegram.jpg + +# original files from merges +*.orig + +# Exclude .exrc file for Vim +.exrc + +# virtual env +venv* + +# environment manager: +.mise.toml \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0da0cea13810c6aae66f28598330e3ef5911a56c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,83 @@ +# Make sure that the additional_dependencies here match pyproject.toml + +ci: + autofix_prs: false + autoupdate_schedule: quarterly + autoupdate_commit_msg: 'Bump `pre-commit` Hooks to Latest Versions' + +repos: +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: 'v0.5.6' + hooks: + - id: ruff + name: ruff + additional_dependencies: + - httpx~=0.27 + - tornado~=6.4 + - APScheduler~=3.10.4 + - cachetools>=5.3.3,<5.5.0 + - aiolimiter~=1.1.0 +- repo: https://github.com/psf/black-pre-commit-mirror + rev: 24.4.2 + hooks: + - id: black + args: + - --diff + - --check +- repo: https://github.com/PyCQA/flake8 + rev: 7.1.0 + hooks: + - id: flake8 +- repo: https://github.com/PyCQA/pylint + rev: v3.2.4 + hooks: + - id: pylint + files: ^(?!(tests|docs)).*\.py$ + additional_dependencies: + - httpx~=0.27 + - tornado~=6.4 + - APScheduler~=3.10.4 + - cachetools>=5.3.3,<5.5.0 + - aiolimiter~=1.1.0 + - . # this basically does `pip install -e .` +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.10.1 + hooks: + - id: mypy + name: mypy-ptb + files: ^(?!(tests|examples|docs)).*\.py$ + additional_dependencies: + - types-pytz + - types-cryptography + - types-cachetools + - httpx~=0.27 + - tornado~=6.4 + - APScheduler~=3.10.4 + - cachetools>=5.3.3,<5.5.0 + - aiolimiter~=1.1.0 + - . # this basically does `pip install -e .` + - id: mypy + name: mypy-examples + files: ^examples/.*\.py$ + args: + - --no-strict-optional + - --follow-imports=silent + additional_dependencies: + - tornado~=6.4 + - APScheduler~=3.10.4 + - cachetools>=5.3.3,<5.5.0 + - . # this basically does `pip install -e .` +- repo: https://github.com/asottile/pyupgrade + rev: v3.16.0 + hooks: + - id: pyupgrade + args: + - --py38-plus +- repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + name: isort + args: + - --diff + - --check diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000000000000000000000000000000000000..a23c582637d063c9e11237c90063a9cbe5320b49 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,62 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/source/conf.py + +# Optionally build your docs in additional formats such as PDF +formats: + - pdf + +# Optionally set the version of Python and requirements required to build your docs +python: + install: + - method: pip + path: . + - requirements: docs/requirements-docs.txt + +build: + os: ubuntu-22.04 + tools: + python: "3" # latest stable cpython version + jobs: + post_build: + # Based on https://github.com/readthedocs/readthedocs.org/issues/3242#issuecomment-1410321534 + # This provides a HTML zip file for download, with the same structure as the hosted website + - mkdir --parents $READTHEDOCS_OUTPUT/htmlzip + - cp --recursive $READTHEDOCS_OUTPUT/html $READTHEDOCS_OUTPUT/$READTHEDOCS_PROJECT + # Hide the "other versions" dropdown. This is a workaround for those versions being shown, + # but not being accessible, as they are not built. Also, they hide the actual sidebar menu + # that is relevant only on ReadTheDocs. + - echo "#furo-readthedocs-versions{display:none}" >> $READTHEDOCS_OUTPUT/$READTHEDOCS_PROJECT/_static/styles/furo-extensions.css + - cd $READTHEDOCS_OUTPUT ; zip --recurse-path --symlinks htmlzip/$READTHEDOCS_PROJECT.zip $READTHEDOCS_PROJECT + +search: + ranking: # bump up rank of commonly searched pages: (default: 0, values range from -10 to 10) + telegram.bot.html: 7 + telegram.message.html: 3 + telegram.update.html: 3 + telegram.user.html: 2 + telegram.chat.html: 2 + telegram.ext.application.html: 3 + telegram.ext.filters.html: 3 + telegram.ext.callbackcontext.html: 2 + telegram.ext.inlinekeyboardbutton.html: 1 + + telegram.passport*.html: -7 + + ignore: + - changelog.html + - coc.html + - bot_methods.html# + - bot_methods.html + # Defaults + - search.html + - search/index.html + - 404.html + - 404/index.html' diff --git a/AUTHORS.rst b/AUTHORS.rst new file mode 100644 index 0000000000000000000000000000000000000000..e95a2b7a3f979e91d155de2a00d472c1c39a39b3 --- /dev/null +++ b/AUTHORS.rst @@ -0,0 +1,137 @@ +Credits +======= + +``python-telegram-bot`` was originally created by +`Leandro Toledo `_. +The current development team includes + +- `Hinrich Mahler `_ (maintainer) +- `Poolitzer `_ (community liaison) +- `Shivam `_ +- `Harshil `_ +- `Dmitry Kolomatskiy `_ +- `Aditya `_ + +Emeritus maintainers include +`Jannes Höke `_ (`@jh0ker `_ on Telegram), +`Noam Meltzer `_, `Pieter Schutz `_ and `Jasmin Bom `_. + +Contributors +------------ + +The following wonderful people contributed directly or indirectly to this project: + +- `Abdelrahman `_ +- `Abshar `_ +- `Alateas `_ +- `Ales Dokshanin `_ +- `Alexandre `_ +- `Alizia `_ +- `Ambro17 `_ +- `Andrej Zhilenkov `_ +- `Anton Tagunov `_ +- `Avanatiker `_ +- `Balduro `_ +- `Bibo-Joshi `_ +- `Biruk Alamirew `_ +- `bimmlerd `_ +- `cyc8 `_ +- `d-qoi `_ +- `daimajia `_ +- `Daniel Reed `_ +- `D David Livingston `_ +- `DonalDuck004 `_ +- `Eana Hufwe `_ +- `Ehsan Online `_ +- `Eldad Carin `_ +- `Eli Gao `_ +- `Emilio Molinari `_ +- `ErgoZ Riftbit Vaper `_ +- `Eugene Lisitsky `_ +- `Eugenio Panadero `_ +- `Evan Haberecht `_ +- `Evgeny Denisov `_ +- `evgfilim1 `_ +- `ExalFabu `_ +- `franciscod `_ +- `gamgi `_ +- `Gauthamram Ravichandran `_ +- `Harshil `_ +- `Hugo Damer `_ +- `ihoru `_ +- `Iulian Onofrei `_ +- `Jainam Oswal `_ +- `Jasmin Bom `_ +- `JASON0916 `_ +- `jeffffc `_ +- `Jelle Besseling `_ +- `jh0ker `_ +- `jlmadurga `_ +- `John Yong `_ +- `Joscha Götzer `_ +- `jossalgon `_ +- `JRoot3D `_ +- `kenjitagawa `_ +- `kennethcheo `_ +- `Kirill Vasin `_ +- `Kjwon15 `_ +- `Li-aung Yip `_ +- `Loo Zheng Yuan `_ +- `LRezende `_ +- `Luca Bellanti `_ +- `Lucas Molinari `_ +- `macrojames `_ +- `Matheus Lemos `_ +- `Michael Dix `_ +- `Michael Elovskikh `_ +- `Miguel C. R. `_ +- `miles `_ +- `Mischa Krüger `_ +- `Mohd Yusuf `_ +- `naveenvhegde `_ +- `neurrone `_ +- `NikitaPirate `_ +- `Nikolai Krivenko `_ +- `njittam `_ +- `Noam Meltzer `_ +- `Oleg Shlyazhko `_ +- `Oleg Sushchenko `_ +- `Or Bin `_ +- `overquota `_ +- `Pablo Martinez `_ +- `Paradox `_ +- `Patrick Hofmann `_ +- `Paul Larsen `_ +- `Pawan `_ +- `Pieter Schutz `_ +- `Piraty `_ +- `Poolitzer `_ +- `Pranjalya Tiwari `_ +- `Rahiel Kasim `_ +- `Riko Naka `_ +- `Rizlas `_ +- `Sahil Sharma `_ +- `Sam Mosleh `_ +- `Sascha `_ +- `Shelomentsev D `_ +- `Shivam Saini `_ +- `Simon Schürrle `_ +- `sooyhwang `_ +- `syntx `_ +- `thodnev `_ +- `Timur Kushukov `_ +- `Trainer Jono `_ +- `Valentijn `_ +- `voider1 `_ +- `Vorobjev Simon `_ +- `Wagner Macedo `_ +- `wjt `_ +- `Wonseok Oh `_ +- `Yaw Danso `_ +- `Yao Kuan `_ +- `zeroone2numeral2 `_ +- `zeshuaro `_ +- `zpavloudis `_ + + +Please add yourself here alphabetically when you submit your first pull request. diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 0000000000000000000000000000000000000000..8e5f302dd034f70307918ab803e9bb0efc14dd66 --- /dev/null +++ b/CHANGES.rst @@ -0,0 +1,2630 @@ +.. _ptb-changelog: + +========= +Changelog +========= + +Version 21.5 +============ + +*Released 2024-09-01* + +This is the technical changelog for version 21.5. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes +------------- + +- Full Support for Bot API 7.9 (:pr:`4429`) +- Full Support for Bot API 7.8 (:pr:`4408`) + +New Features +------------ + +- Add ``MessageEntity.shift_entities`` and ``MessageEntity.concatenate`` (:pr:`4376` closes :issue:`4372`) +- Add Parameter ``game_pattern`` to ``CallbackQueryHandler`` (:pr:`4353` by `jainamoswal `_ closes :issue:`4269`) +- Add Parameter ``read_file_handle`` to ``InputFile`` (:pr:`4388` closes :issue:`4339`) + +Documentation Improvements +-------------------------- + +- Bugfix for "Available In" Admonitions (:pr:`4413`) +- Documentation Improvements (:pr:`4400` closes :issue:`4446`, :pr:`4448` by `Palaptin `_) +- Document Return Types of ``RequestData`` Members (:pr:`4396`) +- Add Introductory Paragraphs to Telegram Types Subsections (:pr:`4389` by `mohdyusuf2312 `_ closes :issue:`4380`) +- Start Adapting to RTD Addons (:pr:`4386`) + +Minor and Internal Changes +--------------------------- + +- Remove Surplus Logging from ``Updater`` Network Loop (:pr:`4432` by `MartinHjelmare `_) +- Add Internal Constants for Encodings (:pr:`4378` by `elpekenin `_) +- Improve PyPI Automation (:pr:`4375` closes :issue:`4373`) +- Update Test Suite to New Test Channel Setup (:pr:`4435`) +- Improve Fixture Usage in ``test_message.py`` (:pr:`4431` by `Palaptin `_) +- Update Python 3.13 Test Suite to RC1 (:pr:`4415`) +- Bump ``ruff`` and Add New Rules (:pr:`4416`) + +Dependency Updates +------------------ + +- Update ``cachetools`` requirement from <5.5.0,>=5.3.3 to >=5.3.3,<5.6.0 (:pr:`4437`) +- Bump ``sphinx`` from 7.4.7 to 8.0.2 and ``furo`` from 2024.7.18 to 2024.8.6 (:pr:`4412`) +- Bump ``test-summary/action`` from 2.3 to 2.4 (:pr:`4410`) +- Bump ``pytest`` from 8.2.2 to 8.3.2 (:pr:`4403`) +- Bump ``dependabot/fetch-metadata`` from 2.1.0 to 2.2.0 (:pr:`4411`) +- Update ``cachetools`` requirement from ~=5.3.3 to >=5.3.3,<5.5.0 (:pr:`4390`) +- Bump ``sphinx`` from 7.3.7 to 7.4.7 (:pr:`4395`) +- Bump ``furo`` from 2024.5.6 to 2024.7.18 (:pr:`4392`) + +Version 21.4 +============ + +*Released 2024-07-12* + +This is the technical changelog for version 21.4. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes +------------- + +- Full Support for Bot API 7.5 (:pr:`4328`, :pr:`4316`, :pr:`4315`, :pr:`4312` closes :issue:`4310`, :pr:`4311`) +- Full Support for Bot API 7.6 (:pr:`4333` closes :issue:`4331`, :pr:`4344`, :pr:`4341`, :pr:`4334`, :pr:`4335`, :pr:`4351`, :pr:`4342`, :pr:`4348`) +- Full Support for Bot API 7.7 (:pr:`4356` closes :issue:`4355`) +- Drop ``python-telegram-bot-raw`` And Switch to ``pyproject.toml`` Based Packaging (:pr:`4288` closes :issue:`4129` and :issue:`4296`) +- Deprecate Inclusion of ``successful_payment`` in ``Message.effective_attachment`` (:pr:`4365` closes :issue:`4350`) + +New Features +------------ + +- Add Support for Python 3.13 Beta (:pr:`4253`) +- Add ``filters.PAID_MEDIA`` (:pr:`4357`) +- Log Received Data on Deserialization Errors (:pr:`4304`) +- Add ``MessageEntity.adjust_message_entities_to_utf_16`` Utility Function (:pr:`4323` by `Antares0982 `_ closes :issue:`4319`) +- Make Argument ``bot`` of ``TelegramObject.de_json`` Optional (:pr:`4320`) + +Documentation Improvements +-------------------------- + +- Documentation Improvements (:pr:`4303` closes :issue:`4301`) +- Restructure Readme (:pr:`4362`) +- Fix Link-Check Workflow (:pr:`4332`) + +Internal Changes +---------------- + +- Automate PyPI Releases (:pr:`4364` closes :issue:`4318`) +- Add ``mise-en-place`` to ``.gitignore`` (:pr:`4300`) +- Use a Composite Action for Testing Type Completeness (:pr:`4367`) +- Stabilize Some Concurrency Usages in Test Suite (:pr:`4360`) +- Add a Test Case for ``MenuButton`` (:pr:`4363`) +- Extend ``SuccessfulPayment`` Test (:pr:`4349`) +- Small Fixes for ``test_stars.py`` (:pr:`4347`) +- Use Python 3.13 Beta 3 in Test Suite (:pr:`4336`) + +Dependency Updates +------------------ + +- Bump ``ruff`` and Add New Rules (:pr:`4329`) +- Bump ``pre-commit`` Hooks to Latest Versions (:pr:`4337`) +- Add Lower Bound for ``flaky`` Dependency (:pr:`4322` by `Palaptin `_) +- Bump ``pytest`` from 8.2.1 to 8.2.2 (:pr:`4294`) + +Version 21.3 +============ +*Released 2024-06-07* + +This is the technical changelog for version 21.3. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes +------------- + +- Full Support for Bot API 7.4 (:pr:`4286`, :pr:`4276` closes :issue:`4275`, :pr:`4285`, :pr:`4283`, :pr:`4280`, :pr:`4278`, :pr:`4279`) +- Deprecate ``python-telegram-bot-raw`` (:pr:`4270`) +- Remove Functionality Deprecated in Bot API 7.3 (:pr:`4266` closes :issue:`4244`) + +New Features +------------ + +- Add Parameter ``chat_id`` to ``ChatMemberHandler`` (:pr:`4290` by `uniquetrij `_ closes :issue:`4287`) + +Documentation Improvements +-------------------------- + +- Documentation Improvements (:pr:`4264` closes :issue:`4240`) + +Internal Changes +---------------- + +- Add ``setuptools`` to ``requirements-dev.txt`` (:pr:`4282`) +- Update Settings for pre-commit.ci (:pr:`4265`) + +Dependency Updates +------------------ + +- Bump ``pytest`` from 8.2.0 to 8.2.1 (:pr:`4272`) + +Version 21.2 +============ + +*Released 2024-05-20* + +This is the technical changelog for version 21.2. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes +------------- + +- Full Support for Bot API 7.3 (:pr:`4246`, :pr:`4260`, :pr:`4243`, :pr:`4248`, :pr:`4242` closes :issue:`4236`, :pr:`4247` by `aelkheir `_) +- Remove Functionality Deprecated by Bot API 7.2 (:pr:`4245`) + +New Features +------------ + +- Add Version to ``PTBDeprecationWarning`` (:pr:`4262` closes :issue:`4261`) +- Handle Exceptions in building ``CallbackContext`` (:pr:`4222`) + +Bug Fixes +--------- + +- Call ``Application.post_stop`` Only if ``Application.stop`` was called (:pr:`4211` closes :issue:`4210`) +- Handle ``SystemExit`` raised in Handlers (:pr:`4157` closes :issue:`4155` and :issue:`4156`) +- Make ``Birthdate.to_date`` Return a ``datetime.date`` Object (:pr:`4251`) + +Documentation Improvements +-------------------------- + +- Documentation Improvements (:pr:`4217`) + +Internal Changes +---------------- + +- Add New Rules to ``ruff`` Config (:pr:`4250`) +- Adapt Test Suite to Changes in Error Messages (:pr:`4238`) + +Dependency Updates +------------------ + +- Bump ``furo`` from 2024.4.27 to 2024.5.6 (:pr:`4252`) +- ``pre-commit`` autoupdate (:pr:`4239`) +- Bump ``pytest`` from 8.1.1 to 8.2.0 (:pr:`4231`) +- Bump ``dependabot/fetch-metadata`` from 2.0.0 to 2.1.0 (:pr:`4228`) +- Bump ``pytest-asyncio`` from 0.21.1 to 0.21.2 (:pr:`4232`) +- Bump ``pytest-xdist`` from 3.6.0 to 3.6.1 (:pr:`4233`) +- Bump ``furo`` from 2024.1.29 to 2024.4.27 (:pr:`4230`) +- Bump ``srvaroa/labeler`` from 1.10.0 to 1.10.1 (:pr:`4227`) +- Bump ``pytest`` from 7.4.4 to 8.1.1 (:pr:`4218`) +- Bump ``sphinx`` from 7.2.6 to 7.3.7 (:pr:`4215`) +- Bump ``pytest-xdist`` from 3.5.0 to 3.6.0 (:pr:`4215`) + +Version 21.1.1 +============== + +*Released 2024-04-15* + +This is the technical changelog for version 21.1.1. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `__. + +Bug Fixes +--------- + +- Fix Bug With Parameter ``message_thread_id`` of ``Message.reply_*`` (:pr:`4207` closes :issue:`4205`) + +Minor Changes +------------- + +- Remove Deprecation Warning in ``JobQueue.run_daily`` (:pr:`4206` by `@Konano `__) +- Fix Annotation of ``EncryptedCredentials.decrypted_secret`` (:pr:`4199` by `@marinelay `__ closes :issue:`4198`) + + +Version 21.1 +============== + +*Released 2024-04-12* + +This is the technical changelog for version 21.1. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `__. + +Major Changes +------------- + +- API 7.2 (:pr:`4180` closes :issue:`4179` and :issue:`4181`, :issue:`4181`) +- Make ``ChatAdministratorRights/ChatMemberAdministrator.can_*_stories`` Required (API 7.1) (:pr:`4192`) + +Minor Changes +------------- + +- Refactor Debug logging in ``Bot`` to Improve Type Hinting (:pr:`4151` closes :issue:`4010`) + +New Features +------------ + +- Make ``Message.reply_*`` Reply in the Same Topic by Default (:pr:`4170` by `@aelkheir `__ closes :issue:`4139`) +- Accept Socket Objects for Webhooks (:pr:`4161` closes :issue:`4078`) +- Add ``Update.effective_sender`` (:pr:`4168` by `@aelkheir `__ closes :issue:`4085`) + +Documentation Improvements +-------------------------- + +- Documentation Improvements (:pr:`4171`, :pr:`4158` by `@teslaedison `__) + +Internal Changes +---------------- + +- Temporarily Mark Tests with ``get_sticker_set`` as XFAIL due to API 7.2 Update (:pr:`4190`) + +Dependency Updates +------------------ + +- ``pre-commit`` autoupdate (:pr:`4184`) +- Bump ``dependabot/fetch-metadata`` from 1.6.0 to 2.0.0 (:pr:`4185`) + + +Version 21.0.1 +============== + +*Released 2024-03-06* + +This is the technical changelog for version 21.0.1. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `__. + +Bug Fixes +--------- + +- Remove ``docs`` from Package (:pr:`4150`) + + +Version 21.0 +============ + +*Released 2024-03-06* + +This is the technical changelog for version 21.0. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `__. + +Major Changes +------------- + +- Remove Functionality Deprecated in API 7.0 (:pr:`4114` closes :issue:`4099`) +- API 7.1 (:pr:`4118`) + +New Features +------------ + +- Add Parameter ``media_write_timeout`` to ``HTTPXRequest`` and Method ``ApplicationBuilder.media_write_timeout`` (:pr:`4120` closes :issue:`3864`) +- Handle Properties in ``TelegramObject.__setstate__`` (:pr:`4134` closes :issue:`4111`) + +Bug Fixes +--------- + +- Add Missing Slot to ``Updater`` (:pr:`4130` closes :issue:`4127`) + +Documentation Improvements +-------------------------- + +- Improve HTML Download of Documentation (:pr:`4146` closes :issue:`4050`) +- Documentation Improvements (:pr:`4109`, :issue:`4116`) +- Update Copyright to 2024 (:pr:`4121` by `@aelkheir `__ closes :issue:`4041`) + +Internal Changes +---------------- + +- Apply ``pre-commit`` Checks More Widely (:pr:`4135`) +- Refactor and Overhaul ``test_official`` (:pr:`4087` closes :issue:`3874`) +- Run Unit Tests in PRs on Requirements Changes (:pr:`4144`) +- Make ``Updater.stop`` Independent of ``CancelledError`` (:pr:`4126`) + +Dependency Updates +------------------ + +- Relax Upper Bound for ``httpx`` Dependency (:pr:`4148`) +- Bump ``test-summary/action`` from 2.2 to 2.3 (:pr:`4142`) +- Update ``cachetools`` requirement from ~=5.3.2 to ~=5.3.3 (:pr:`4141`) +- Update ``httpx`` requirement from ~=0.26.0 to ~=0.27.0 (:pr:`4131`) + + +Version 20.8 +============ + +*Released 2024-02-08* + +This is the technical changelog for version 20.8. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `__. + +Major Changes +------------- + +- API 7.0 (:pr:`4034` closes :issue:`4033`, :pr:`4038` by `@aelkheir `__) + +Minor Changes +------------- + +- Fix Type Hint for ``filters`` Parameter of ``MessageHandler`` (:pr:`4039` by `@Palaptin `__) +- Deprecate ``filters.CHAT`` (:pr:`4083` closes :issue:`4062`) +- Improve Error Handling in Built-In Webhook Handler (:pr:`3987` closes :issue:`3979`) + +New Features +------------ + +- Add Parameter ``pattern`` to ``PreCheckoutQueryHandler`` and ``filters.SuccessfulPayment`` (:pr:`4005` by `@aelkheir `__ closes :issue:`3752`) +- Add Missing Conversions of ``type`` to Corresponding Enum from ``telegram.constants`` (:pr:`4067`) +- Add Support for Unix Sockets to ``Updater.start_webhook`` (:pr:`3986` closes :issue:`3978`) +- Add ``Bot.do_api_request`` (:pr:`4084` closes :issue:`4053`) +- Add ``AsyncContextManager`` as Parent Class to ``BaseUpdateProcessor`` (:pr:`4001`) + +Documentation Improvements +-------------------------- + +- Documentation Improvements (:pr:`3919`) +- Add Docstring to Dunder Methods (:pr:`3929` closes :issue:`3926`) +- Documentation Improvements (:pr:`4002`, :pr:`4079` by `@kenjitagawa `__, :pr:`4104` by `@xTudoS `__) + +Internal Changes +---------------- + +- Drop Usage of DeepSource (:pr:`4100`) +- Improve Type Completeness & Corresponding Workflow (:pr:`4035`) +- Bump ``ruff`` and Remove ``sort-all`` (:pr:`4075`) +- Move Handler Files to ``_handlers`` Subdirectory (:pr:`4064` by `@lucasmolinari `__ closes :issue:`4060`) +- Introduce ``sort-all`` Hook for ``pre-commit`` (:pr:`4052`) +- Use Recommended ``pre-commit`` Mirror for ``black`` (:pr:`4051`) +- Remove Unused ``DEFAULT_20`` (:pr:`3997`) +- Migrate From ``setup.cfg`` to ``pyproject.toml`` Where Possible (:pr:`4088`) + +Dependency Updates +------------------ + +- Bump ``black`` and ``ruff`` (:pr:`4089`) +- Bump ``srvaroa/labeler`` from 1.8.0 to 1.10.0 (:pr:`4048`) +- Update ``tornado`` requirement from ~=6.3.3 to ~=6.4 (:pr:`3992`) +- Bump ``actions/stale`` from 8 to 9 (:pr:`4046`) +- Bump ``actions/setup-python`` from 4 to 5 (:pr:`4047`) +- ``pre-commit`` autoupdate (:pr:`4101`) +- Bump ``actions/upload-artifact`` from 3 to 4 (:pr:`4045`) +- ``pre-commit`` autoupdate (:pr:`3996`) +- Bump ``furo`` from 2023.9.10 to 2024.1.29 (:pr:`4094`) +- ``pre-commit`` autoupdate (:pr:`4043`) +- Bump ``codecov/codecov-action`` from 3 to 4 (:pr:`4091`) +- Bump ``EndBug/add-and-commit`` from 9.1.3 to 9.1.4 (:pr:`4090`) +- Update ``httpx`` requirement from ~=0.25.2 to ~=0.26.0 (:pr:`4024`) +- Bump ``pytest`` from 7.4.3 to 7.4.4 (:pr:`4056`) +- Bump ``srvaroa/labeler`` from 1.7.0 to 1.8.0 (:pr:`3993`) +- Bump ``test-summary/action`` from 2.1 to 2.2 (:pr:`4044`) +- Bump ``dessant/lock-threads`` from 4.0.1 to 5.0.1 (:pr:`3994`) + + +Version 20.7 +============ + +*Released 2023-11-27* + +This is the technical changelog for version 20.7. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `__. + +New Features +------------ + +- Add ``JobQueue.scheduler_configuration`` and Corresponding Warnings (:pr:`3913` closes :issue:`3837`) +- Add Parameter ``socket_options`` to ``HTTPXRequest`` (:pr:`3935` closes :issue:`2965`) +- Add ``ApplicationBuilder.(get_updates_)socket_options`` (:pr:`3943`) +- Improve ``write_timeout`` Handling for Media Methods (:pr:`3952`) +- Add ``filters.Mention`` (:pr:`3941` closes :issue:`3799`) +- Rename ``proxy_url`` to ``proxy`` and Allow ``httpx.{Proxy, URL}`` as Input (:pr:`3939` closes :issue:`3844`) + +Bug Fixes & Changes +------------------- + +- Adjust ``read_timeout`` Behavior for ``Bot.get_updates`` (:pr:`3963` closes :issue:`3893`) +- Improve ``BaseHandler.__repr__`` for Callbacks without ``__qualname__`` (:pr:`3934`) +- Fix Persistency Issue with Ended Non-Blocking Conversations (:pr:`3962`) +- Improve Type Hinting for Arguments with Default Values in ``Bot`` (:pr:`3942`) + +Documentation Improvements +-------------------------- + +- Add Documentation for ``__aenter__`` and ``__aexit__`` Methods (:pr:`3907` closes :issue:`3886`) +- Improve Insertion of Kwargs into ``Bot`` Methods (:pr:`3965`) + +Internal Changes +---------------- + +- Adjust Tests to New Error Messages (:pr:`3970`) + +Dependency Updates +------------------ + +- Bump ``pytest-xdist`` from 3.3.1 to 3.4.0 (:pr:`3975`) +- ``pre-commit`` autoupdate (:pr:`3967`) +- Update ``httpx`` requirement from ~=0.25.1 to ~=0.25.2 (:pr:`3983`) +- Bump ``pytest-xdist`` from 3.4.0 to 3.5.0 (:pr:`3982`) +- Update ``httpx`` requirement from ~=0.25.0 to ~=0.25.1 (:pr:`3961`) +- Bump ``srvaroa/labeler`` from 1.6.1 to 1.7.0 (:pr:`3958`) +- Update ``cachetools`` requirement from ~=5.3.1 to ~=5.3.2 (:pr:`3954`) +- Bump ``pytest`` from 7.4.2 to 7.4.3 (:pr:`3953`) + + +Version 20.6 +============ + +*Released 2023-10-03* + +This is the technical changelog for version 20.6. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `__. + +Major Changes +------------- + +- Drop Backward Compatibility Layer Introduced in :pr:`3853` (API 6.8) (:pr:`3873`) +- Full Support for Bot API 6.9 (:pr:`3898`) + +New Features +------------ + +- Add Rich Equality Comparison to ``WriteAccessAllowed`` (:pr:`3911` closes :issue:`3909`) +- Add ``__repr__`` Methods Added in :pr:`3826` closes :issue:`3770` to Sphinx Documentation (:pr:`3901` closes :issue:`3889`) +- Add String Representation for Selected Classes (:pr:`3826` closes :issue:`3770`) + +Minor Changes +------------- + +- Add Support Python 3.12 (:pr:`3915`) +- Documentation Improvements (:pr:`3910`) + +Internal Changes +---------------- + +- Verify Type Hints for Bot Method & Telegram Class Parameters (:pr:`3868`) +- Move Bot API Tests to Separate Workflow File (:pr:`3912`) +- Fix Failing ``file_size`` Tests (:pr:`3906`) +- Set Threshold for DeepSource’s PY-R1000 to High (:pr:`3888`) +- One-Time Code Formatting Improvement via ``--preview`` Flag of ``black`` (:pr:`3882`) +- Move Dunder Methods to the Top of Class Bodies (:pr:`3883`) +- Remove Superfluous ``Defaults.__ne__`` (:pr:`3884`) + +Dependency Updates +------------------ + +- ``pre-commit`` autoupdate (:pr:`3876`) +- Update ``pre-commit`` Dependencies (:pr:`3916`) +- Bump ``actions/checkout`` from 3 to 4 (:pr:`3914`) +- Update ``httpx`` requirement from ~=0.24.1 to ~=0.25.0 (:pr:`3891`) +- Bump ``furo`` from 2023.8.19 to 2023.9.10 (:pr:`3890`) +- Bump ``sphinx`` from 7.2.5 to 7.2.6 (:pr:`3892`) +- Update ``tornado`` requirement from ~=6.2 to ~=6.3.3 (:pr:`3675`) +- Bump ``pytest`` from 7.4.0 to 7.4.2 (:pr:`3881`) + + +Version 20.5 +============ +*Released 2023-09-03* + +This is the technical changelog for version 20.5. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `__. + +Major Changes +------------- + +- API 6.8 (:pr:`3853`) +- Remove Functionality Deprecated Since Bot API 6.5, 6.6 or 6.7 (:pr:`3858`) + +New Features +------------ + +- Extend Allowed Values for HTTP Version (:pr:`3823` closes :issue:`3821`) +- Add ``has_args`` Parameter to ``CommandHandler`` (:pr:`3854` by `@thatguylah `__ closes :issue:`3798`) +- Add ``Application.stop_running()`` and Improve Marking Updates as Read on ``Updater.stop()`` (:pr:`3804`) + +Minor Changes +------------- + +- Type Hinting Fixes for ``WebhookInfo`` (:pr:`3871`) +- Test and Document ``Exception.__cause__`` on ``NetworkError`` (:pr:`3792` closes :issue:`3778`) +- Add Support for Python 3.12 RC (:pr:`3847`) + +Documentation Improvements +-------------------------- + +- Remove Version Check from Examples (:pr:`3846`) +- Documentation Improvements (:pr:`3803`, :pr:`3797`, :pr:`3816` by `@trim21 `__, :pr:`3829` by `@aelkheir `__) +- Provide Versions of ``customwebhookbot.py`` with Different Frameworks (:pr:`3820` closes :issue:`3717`) + +Dependency Updates +------------------ + +- ``pre-commit`` autoupdate (:pr:`3824`) +- Bump ``srvaroa/labeler`` from 1.6.0 to 1.6.1 (:pr:`3870`) +- Bump ``sphinx`` from 7.0.1 to 7.1.1 (:pr:`3818`) +- Bump ``sphinx`` from 7.2.3 to 7.2.5 (:pr:`3869`) +- Bump ``furo`` from 2023.5.20 to 2023.7.26 (:pr:`3817`) +- Update ``apscheduler`` requirement from ~=3.10.3 to ~=3.10.4 (:pr:`3862`) +- Bump ``sphinx`` from 7.2.2 to 7.2.3 (:pr:`3861`) +- Bump ``pytest-asyncio`` from 0.21.0 to 0.21.1 (:pr:`3801`) +- Bump ``sphinx-paramlinks`` from 0.5.4 to 0.6.0 (:pr:`3840`) +- Update ``apscheduler`` requirement from ~=3.10.1 to ~=3.10.3 (:pr:`3851`) +- Bump ``furo`` from 2023.7.26 to 2023.8.19 (:pr:`3850`) +- Bump ``sphinx`` from 7.1.2 to 7.2.2 (:pr:`3852`) +- Bump ``sphinx`` from 7.1.1 to 7.1.2 (:pr:`3827`) + + +Version 20.4 +============ + +*Released 2023-07-09* + +This is the technical changelog for version 20.4. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `__. + +Major Changes +------------- + +- Drop Support for Python 3.7 (:pr:`3728`, :pr:`3742` by `@Trifase `__, :pr:`3749` by `@thefunkycat `__, :pr:`3740` closes :issue:`3732`, :pr:`3754` closes :issue:`3731`, :pr:`3753`, :pr:`3764`, :pr:`3762`, :pr:`3759` closes :issue:`3733`) + +New Features +------------ + +- Make Integration of ``APScheduler`` into ``JobQueue`` More Explicit (:pr:`3695`) +- Introduce ``BaseUpdateProcessor`` for Customized Concurrent Handling of Updates (:pr:`3654` closes :issue:`3509`) + +Minor Changes +------------- + +- Fix Inconsistent Type Hints for ``timeout`` Parameter of ``Bot.get_updates`` (:pr:`3709` by `@revolter `__) +- Use Explicit Optionals (:pr:`3692` by `@MiguelX413 `__) + +Bug Fixes +--------- + +- Fix Wrong Warning Text in ``KeyboardButton.__eq__`` (:pr:`3768`) + +Documentation Improvements +-------------------------- + +- Explicitly set ``allowed_updates`` in Examples (:pr:`3741` by `@Trifase `__ closes :issue:`3726`) +- Bump ``furo`` and ``sphinx`` (:pr:`3719`) +- Documentation Improvements (:pr:`3698`, :pr:`3708` by `@revolter `__, :pr:`3767`) +- Add Quotes for Installation Instructions With Optional Dependencies (:pr:`3780`) +- Exclude Type Hints from Stability Policy (:pr:`3712`) +- Set ``httpx`` Logging Level to Warning in Examples (:pr:`3746` closes :issue:`3743`) + +Internal Changes +---------------- + +- Drop a Legacy ``pre-commit.ci`` Configuration (:pr:`3697`) +- Add Python 3.12 Beta to the Test Matrix (:pr:`3751`) +- Use Temporary Files for Testing File Downloads (:pr:`3777`) +- Auto-Update Changed Version in Other Files After Dependabot PRs (:pr:`3716`) +- Add More ``ruff`` Rules (:pr:`3763`) +- Rename ``_handler.py`` to ``_basehandler.py`` (:pr:`3761`) +- Automatically Label ``pre-commit-ci`` PRs (:pr:`3713`) +- Rework ``pytest`` Integration into GitHub Actions (:pr:`3776`) +- Fix Two Bugs in GitHub Actions Workflows (:pr:`3739`) + +Dependency Updates +------------------ + +- Update ``cachetools`` requirement from ~=5.3.0 to ~=5.3.1 (:pr:`3738`) +- Update ``aiolimiter`` requirement from ~=1.0.0 to ~=1.1.0 (:pr:`3707`) +- ``pre-commit`` autoupdate (:pr:`3791`) +- Bump ``sphinxcontrib-mermaid`` from 0.8.1 to 0.9.2 (:pr:`3737`) +- Bump ``pytest-xdist`` from 3.2.1 to 3.3.0 (:pr:`3705`) +- Bump ``srvaroa/labeler`` from 1.5.0 to 1.6.0 (:pr:`3786`) +- Bump ``dependabot/fetch-metadata`` from 1.5.1 to 1.6.0 (:pr:`3787`) +- Bump ``dessant/lock-threads`` from 4.0.0 to 4.0.1 (:pr:`3785`) +- Bump ``pytest`` from 7.3.2 to 7.4.0 (:pr:`3774`) +- Update ``httpx`` requirement from ~=0.24.0 to ~=0.24.1 (:pr:`3715`) +- Bump ``pytest-xdist`` from 3.3.0 to 3.3.1 (:pr:`3714`) +- Bump ``pytest`` from 7.3.1 to 7.3.2 (:pr:`3758`) +- ``pre-commit`` autoupdate (:pr:`3747`) + + +Version 20.3 +============ +*Released 2023-05-07* + +This is the technical changelog for version 20.3. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes +------------- + +- Full support for API 6.7 (:pr:`3673`) +- Add a Stability Policy (:pr:`3622`) + +New Features +------------ + +- Add ``Application.mark_data_for_update_persistence`` (:pr:`3607`) +- Make ``Message.link`` Point to Thread View Where Possible (:pr:`3640`) +- Localize Received ``datetime`` Objects According to ``Defaults.tzinfo`` (:pr:`3632`) + +Minor Changes, Documentation Improvements and CI +------------------------------------------------ + +- Empower ``ruff`` (:pr:`3594`) +- Drop Usage of ``sys.maxunicode`` (:pr:`3630`) +- Add String Representation for ``RequestParameter`` (:pr:`3634`) +- Stabilize CI by Rerunning Failed Tests (:pr:`3631`) +- Give Loggers Better Names (:pr:`3623`) +- Add Logging for Invalid JSON Data in ``BasePersistence.parse_json_payload`` (:pr:`3668`) +- Improve Warning Categories & Stacklevels (:pr:`3674`) +- Stabilize ``test_delete_sticker_set`` (:pr:`3685`) +- Shield Update Fetcher Task in ``Application.start`` (:pr:`3657`) +- Recover 100% Type Completeness (:pr:`3676`) +- Documentation Improvements (:pr:`3628`, :pr:`3636`, :pr:`3694`) + +Dependencies +------------ + +- Bump ``actions/stale`` from 7 to 8 (:pr:`3644`) +- Bump ``furo`` from 2023.3.23 to 2023.3.27 (:pr:`3643`) +- ``pre-commit`` autoupdate (:pr:`3646`, :pr:`3688`) +- Remove Deprecated ``codecov`` Package from CI (:pr:`3664`) +- Bump ``sphinx-copybutton`` from 0.5.1 to 0.5.2 (:pr:`3662`) +- Update ``httpx`` requirement from ~=0.23.3 to ~=0.24.0 (:pr:`3660`) +- Bump ``pytest`` from 7.2.2 to 7.3.1 (:pr:`3661`) + +Version 20.2 +============ +*Released 2023-03-25* + +This is the technical changelog for version 20.2. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes +------------- +- Full Support for API 6.6 (:pr:`3584`) +- Revert to HTTP/1.1 as Default and make HTTP/2 an Optional Dependency (:pr:`3576`) + +Minor Changes, Documentation Improvements and CI +------------------------------------------------ +- Documentation Improvements (:pr:`3565`, :pr:`3600`) +- Handle Symbolic Links in ``was_called_by`` (:pr:`3552`) +- Tidy Up Tests Directory (:pr:`3553`) +- Enhance ``Application.create_task`` (:pr:`3543`) +- Make Type Completeness Workflow Usable for ``PRs`` from Forks (:pr:`3551`) +- Refactor and Overhaul the Test Suite (:pr:`3426`) + +Dependencies +------------ +- Bump ``pytest-asyncio`` from 0.20.3 to 0.21.0 (:pr:`3624`) +- Bump ``furo`` from 2022.12.7 to 2023.3.23 (:pr:`3625`) +- Bump ``pytest-xdist`` from 3.2.0 to 3.2.1 (:pr:`3606`) +- ``pre-commit`` autoupdate (:pr:`3577`) +- Update ``apscheduler`` requirement from ~=3.10.0 to ~=3.10.1 (:pr:`3572`) +- Bump ``pytest`` from 7.2.1 to 7.2.2 (:pr:`3573`) +- Bump ``pytest-xdist`` from 3.1.0 to 3.2.0 (:pr:`3550`) +- Bump ``sphinxcontrib-mermaid`` from 0.7.1 to 0.8 (:pr:`3549`) + +Version 20.1 +============ +*Released 2023-02-09* + +This is the technical changelog for version 20.1. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes +------------- + +- Full Support for Bot API 6.5 (:pr:`3530`) + +New Features +------------ + +- Add ``Application(Builder).post_stop`` (:pr:`3466`) +- Add ``Chat.effective_name`` Convenience Property (:pr:`3485`) +- Allow to Adjust HTTP Version and Use HTTP/2 by Default (:pr:`3506`) + +Documentation Improvements +-------------------------- + +- Enhance ``chatmemberbot`` Example (:pr:`3500`) +- Automatically Generate Cross-Reference Links (:pr:`3501`, :pr:`3529`, :pr:`3523`) +- Add Some Graphic Elements to Docs (:pr:`3535`) +- Various Smaller Improvements (:pr:`3464`, :pr:`3483`, :pr:`3484`, :pr:`3497`, :pr:`3512`, :pr:`3515`, :pr:`3498`) + +Minor Changes, Documentation Improvements and CI +------------------------------------------------ + +- Update Copyright to 2023 (:pr:`3459`) +- Stabilize Tests on Closing and Hiding the General Forum Topic (:pr:`3460`) +- Fix Dependency Warning Typo (:pr:`3474`) +- Cache Dependencies on ``GitHub`` Actions (:pr:`3469`) +- Store Documentation Builts as ``GitHub`` Actions Artifacts (:pr:`3468`) +- Add ``ruff`` to ``pre-commit`` Hooks (:pr:`3488`) +- Improve Warning for ``days`` Parameter of ``JobQueue.run_daily`` (:pr:`3503`) +- Improve Error Message for ``NetworkError`` (:pr:`3505`) +- Lock Inactive Threads Only Once Each Day (:pr:`3510`) +- Bump ``pytest`` from 7.2.0 to 7.2.1 (:pr:`3513`) +- Check for 3D Arrays in ``check_keyboard_type`` (:pr:`3514`) +- Explicit Type Annotations (:pr:`3508`) +- Increase Verbosity of Type Completeness CI Job (:pr:`3531`) +- Fix CI on Python 3.11 + Windows (:pr:`3547`) + +Dependencies +------------ + +- Bump ``actions/stale`` from 6 to 7 (:pr:`3461`) +- Bump ``dessant/lock-threads`` from 3.0.0 to 4.0.0 (:pr:`3462`) +- ``pre-commit`` autoupdate (:pr:`3470`) +- Update ``httpx`` requirement from ~=0.23.1 to ~=0.23.3 (:pr:`3489`) +- Update ``cachetools`` requirement from ~=5.2.0 to ~=5.2.1 (:pr:`3502`) +- Improve Config for ``ruff`` and Bump to ``v0.0.222`` (:pr:`3507`) +- Update ``cachetools`` requirement from ~=5.2.1 to ~=5.3.0 (:pr:`3520`) +- Bump ``isort`` to 5.12.0 (:pr:`3525`) +- Update ``apscheduler`` requirement from ~=3.9.1 to ~=3.10.0 (:pr:`3532`) +- ``pre-commit`` autoupdate (:pr:`3537`) +- Update ``cryptography`` requirement to >=39.0.1 to address Vulnerability (:pr:`3539`) + +Version 20.0 +============ +*Released 2023-01-01* + +This is the technical changelog for version 20.0. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes +------------- + +- Full Support For Bot API 6.4 (:pr:`3449`) + +Minor Changes, Documentation Improvements and CI +------------------------------------------------ + +- Documentation Improvements (:pr:`3428`, :pr:`3423`, :pr:`3429`, :pr:`3441`, :pr:`3404`, :pr:`3443`) +- Allow ``Sequence`` Input for Bot Methods (:pr:`3412`) +- Update Link-Check CI and Replace a Dead Link (:pr:`3456`) +- Freeze Classes Without Arguments (:pr:`3453`) +- Add New Constants (:pr:`3444`) +- Override ``Bot.__deepcopy__`` to Raise ``TypeError`` (:pr:`3446`) +- Add Log Decorator to ``Bot.get_webhook_info`` (:pr:`3442`) +- Add Documentation On Verifying Releases (:pr:`3436`) +- Drop Undocumented ``Job.__lt__`` (:pr:`3432`) + +Dependencies +------------ + +- Downgrade ``sphinx`` to 5.3.0 to Fix Search (:pr:`3457`) +- Bump ``sphinx`` from 5.3.0 to 6.0.0 (:pr:`3450`) + +Version 20.0b0 +============== +*Released 2022-12-15* + +This is the technical changelog for version 20.0b0. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes +------------- + +- Make ``TelegramObject`` Immutable (:pr:`3249`) + +Minor Changes, Documentation Improvements and CI +------------------------------------------------ + +- Reduce Code Duplication in Testing ``Defaults`` (:pr:`3419`) +- Add Notes and Warnings About Optional Dependencies (:pr:`3393`) +- Simplify Internals of ``Bot`` Methods (:pr:`3396`) +- Reduce Code Duplication in Several ``Bot`` Methods (:pr:`3385`) +- Documentation Improvements (:pr:`3386`, :pr:`3395`, :pr:`3398`, :pr:`3403`) + +Dependencies +------------ + +- Bump ``pytest-xdist`` from 3.0.2 to 3.1.0 (:pr:`3415`) +- Bump ``pytest-asyncio`` from 0.20.2 to 0.20.3 (:pr:`3417`) +- ``pre-commit`` autoupdate (:pr:`3409`) + +Version 20.0a6 +============== +*Released 2022-11-24* + +This is the technical changelog for version 20.0a6. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Bug Fixes +--------- + +- Only Persist Arbitrary ``callback_data`` if ``ExtBot.callback_data_cache`` is Present (:pr:`3384`) +- Improve Backwards Compatibility of ``TelegramObjects`` Pickle Behavior (:pr:`3382`) +- Fix Naming and Keyword Arguments of ``File.download_*`` Methods (:pr:`3380`) +- Fix Return Value Annotation of ``Chat.create_forum_topic`` (:pr:`3381`) + +Version 20.0a5 +============== +*Released 2022-11-22* + +This is the technical changelog for version 20.0a5. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes +------------- + +- API 6.3 (:pr:`3346`, :pr:`3343`, :pr:`3342`, :pr:`3360`) +- Explicit ``local_mode`` Setting (:pr:`3154`) +- Make Almost All 3rd Party Dependencies Optional (:pr:`3267`) +- Split ``File.download`` Into ``File.download_to_drive`` And ``File.download_to_memory`` (:pr:`3223`) + +New Features +------------ + +- Add Properties for API Settings of ``Bot`` (:pr:`3247`) +- Add ``chat_id`` and ``username`` Parameters to ``ChatJoinRequestHandler`` (:pr:`3261`) +- Introduce ``TelegramObject.api_kwargs`` (:pr:`3233`) +- Add Two Constants Related to Local Bot API Servers (:pr:`3296`) +- Add ``recursive`` Parameter to ``TelegramObject.to_dict()`` (:pr:`3276`) +- Overhaul String Representation of ``TelegramObject`` (:pr:`3234`) +- Add Methods ``Chat.mention_{html, markdown, markdown_v2}`` (:pr:`3308`) +- Add ``constants.MessageLimit.DEEP_LINK_LENGTH`` (:pr:`3315`) +- Add Shortcut Parameters ``caption``, ``parse_mode`` and ``caption_entities`` to ``Bot.send_media_group`` (:pr:`3295`) +- Add Several New Enums To Constants (:pr:`3351`) + +Bug Fixes +--------- + +- Fix ``CallbackQueryHandler`` Not Handling Non-String Data Correctly With Regex Patterns (:pr:`3252`) +- Fix Defaults Handling in ``Bot.answer_web_app_query`` (:pr:`3362`) + +Documentation Improvements +-------------------------- + +- Update PR Template (:pr:`3361`) +- Document Dunder Methods of ``TelegramObject`` (:pr:`3319`) +- Add Several References to Wiki pages (:pr:`3306`) +- Overhaul Search bar (:pr:`3218`) +- Unify Documentation of Arguments and Attributes of Telegram Classes (:pr:`3217`, :pr:`3292`, :pr:`3303`, :pr:`3312`, :pr:`3314`) +- Several Smaller Improvements (:pr:`3214`, :pr:`3271`, :pr:`3289`, :pr:`3326`, :pr:`3370`, :pr:`3376`, :pr:`3366`) + +Minor Changes, Documentation Improvements and CI +------------------------------------------------ + +- Improve Warning About Unknown ``ConversationHandler`` States (:pr:`3242`) +- Switch from Stale Bot to ``GitHub`` Actions (:pr:`3243`) +- Bump Python 3.11 to RC2 in Test Matrix (:pr:`3246`) +- Make ``Job.job`` a Property and Make ``Jobs`` Hashable (:pr:`3250`) +- Skip ``JobQueue`` Tests on Windows Again (:pr:`3280`) +- Read-Only ``CallbackDataCache`` (:pr:`3266`) +- Type Hinting Fix for ``Message.effective_attachment`` (:pr:`3294`) +- Run Unit Tests in Parallel (:pr:`3283`) +- Update Test Matrix to Use Stable Python 3.11 (:pr:`3313`) +- Don't Edit Objects In-Place When Inserting ``ext.Defaults`` (:pr:`3311`) +- Add a Test for ``MessageAttachmentType`` (:pr:`3335`) +- Add Three New Test Bots (:pr:`3347`) +- Improve Unit Tests Regarding ``ChatMemberUpdated.difference`` (:pr:`3352`) +- Flaky Unit Tests: Use ``pytest`` Marker (:pr:`3354`) +- Fix ``DeepSource`` Issues (:pr:`3357`) +- Handle Lists and Tuples and Datetimes Directly in ``TelegramObject.to_dict`` (:pr:`3353`) +- Update Meta Config (:pr:`3365`) +- Merge ``ChatDescriptionLimit`` Enum Into ``ChatLimit`` (:pr:`3377`) + +Dependencies +------------ + +- Bump ``pytest`` from 7.1.2 to 7.1.3 (:pr:`3228`) +- ``pre-commit`` Updates (:pr:`3221`) +- Bump ``sphinx`` from 5.1.1 to 5.2.3 (:pr:`3269`) +- Bump ``furo`` from 2022.6.21 to 2022.9.29 (:pr:`3268`) +- Bump ``actions/stale`` from 5 to 6 (:pr:`3277`) +- ``pre-commit`` autoupdate (:pr:`3282`) +- Bump ``sphinx`` from 5.2.3 to 5.3.0 (:pr:`3300`) +- Bump ``pytest-asyncio`` from 0.19.0 to 0.20.1 (:pr:`3299`) +- Bump ``pytest`` from 7.1.3 to 7.2.0 (:pr:`3318`) +- Bump ``pytest-xdist`` from 2.5.0 to 3.0.2 (:pr:`3317`) +- ``pre-commit`` autoupdate (:pr:`3325`) +- Bump ``pytest-asyncio`` from 0.20.1 to 0.20.2 (:pr:`3359`) +- Update ``httpx`` requirement from ~=0.23.0 to ~=0.23.1 (:pr:`3373`) + +Version 20.0a4 +============== +*Released 2022-08-27* + +This is the technical changelog for version 20.0a4. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Hot Fixes +--------- + +* Fix a Bug in ``setup.py`` Regarding Optional Dependencies (:pr:`3209`) + +Version 20.0a3 +============== +*Released 2022-08-27* + +This is the technical changelog for version 20.0a3. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes +------------- + +- Full Support for API 6.2 (:pr:`3195`) + +New Features +------------ + +- New Rate Limiting Mechanism (:pr:`3148`) +- Make ``chat/user_data`` Available in Error Handler for Errors in Jobs (:pr:`3152`) +- Add ``Application.post_shutdown`` (:pr:`3126`) + +Bug Fixes +--------- + +- Fix ``helpers.mention_markdown`` for Markdown V1 and Improve Related Unit Tests (:pr:`3155`) +- Add ``api_kwargs`` Parameter to ``Bot.log_out`` and Improve Related Unit Tests (:pr:`3147`) +- Make ``Bot.delete_my_commands`` a Coroutine Function (:pr:`3136`) +- Fix ``ConversationHandler.check_update`` not respecting ``per_user`` (:pr:`3128`) + +Minor Changes, Documentation Improvements and CI +------------------------------------------------ + +- Add Python 3.11 to Test Suite & Adapt Enum Behaviour (:pr:`3168`) +- Drop Manual Token Validation (:pr:`3167`) +- Simplify Unit Tests for ``Bot.send_chat_action`` (:pr:`3151`) +- Drop ``pre-commit`` Dependencies from ``requirements-dev.txt`` (:pr:`3120`) +- Change Default Values for ``concurrent_updates`` and ``connection_pool_size`` (:pr:`3127`) +- Documentation Improvements (:pr:`3139`, :pr:`3153`, :pr:`3135`) +- Type Hinting Fixes (:pr:`3202`) + +Dependencies +------------ + +- Bump ``sphinx`` from 5.0.2 to 5.1.1 (:pr:`3177`) +- Update ``pre-commit`` Dependencies (:pr:`3085`) +- Bump ``pytest-asyncio`` from 0.18.3 to 0.19.0 (:pr:`3158`) +- Update ``tornado`` requirement from ~=6.1 to ~=6.2 (:pr:`3149`) +- Bump ``black`` from 22.3.0 to 22.6.0 (:pr:`3132`) +- Bump ``actions/setup-python`` from 3 to 4 (:pr:`3131`) + +Version 20.0a2 +============== +*Released 2022-06-27* + +This is the technical changelog for version 20.0a2. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes +------------- + +- Full Support for API 6.1 (:pr:`3112`) + +New Features +------------ + +- Add Additional Shortcut Methods to ``Chat`` (:pr:`3115`) +- Mermaid-based Example State Diagrams (:pr:`3090`) + +Minor Changes, Documentation Improvements and CI +------------------------------------------------ + +- Documentation Improvements (:pr:`3103`, :pr:`3121`, :pr:`3098`) +- Stabilize CI (:pr:`3119`) +- Bump ``pyupgrade`` from 2.32.1 to 2.34.0 (:pr:`3096`) +- Bump ``furo`` from 2022.6.4 to 2022.6.4.1 (:pr:`3095`) +- Bump ``mypy`` from 0.960 to 0.961 (:pr:`3093`) + +Version 20.0a1 +============== +*Released 2022-06-09* + +This is the technical changelog for version 20.0a1. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes: +-------------- + +- Drop Support for ``ujson`` and instead ``BaseRequest.parse_json_payload`` (:pr:`3037`, :pr:`3072`) +- Drop ``InputFile.is_image`` (:pr:`3053`) +- Drop Explicit Type conversions in ``__init__`` s (:pr:`3056`) +- Handle List-Valued Attributes More Consistently (:pr:`3057`) +- Split ``{Command, Prefix}Handler`` And Make Attributes Immutable (:pr:`3045`) +- Align Behavior Of ``JobQueue.run_daily`` With ``cron`` (:pr:`3046`) +- Make PTB Specific Keyword-Only Arguments for PTB Specific in Bot methods (:pr:`3035`) +- Adjust Equality Comparisons to Fit Bot API 6.0 (:pr:`3033`) +- Add Tuple Based Version Info (:pr:`3030`) +- Improve Type Annotations for ``CallbackContext`` and Move Default Type Alias to ``ContextTypes.DEFAULT_TYPE`` (:pr:`3017`, :pr:`3023`) +- Rename ``Job.context`` to ``Job.data`` (:pr:`3028`) +- Rename ``Handler`` to ``BaseHandler`` (:pr:`3019`) + +New Features: +------------- + +- Add ``Application.post_init`` (:pr:`3078`) +- Add Arguments ``chat/user_id`` to ``CallbackContext`` And Example On Custom Webhook Setups (:pr:`3059`) +- Add Convenience Property ``Message.id`` (:pr:`3077`) +- Add Example for ``WebApp`` (:pr:`3052`) +- Rename ``telegram.bot_api_version`` to ``telegram.__bot_api_version__`` (:pr:`3030`) + +Bug Fixes: +---------- + +- Fix Non-Blocking Entry Point in ``ConversationHandler`` (:pr:`3068`) +- Escape Backslashes in ``escape_markdown`` (:pr:`3055`) + +Dependencies: +------------- + +- Update ``httpx`` requirement from ~=0.22.0 to ~=0.23.0 (:pr:`3069`) +- Update ``cachetools`` requirement from ~=5.0.0 to ~=5.2.0 (:pr:`3058`, :pr:`3080`) + +Minor Changes, Documentation Improvements and CI: +------------------------------------------------- + +- Move Examples To Documentation (:pr:`3089`) +- Documentation Improvements and Update Dependencies (:pr:`3010`, :pr:`3007`, :pr:`3012`, :pr:`3067`, :pr:`3081`, :pr:`3082`) +- Improve Some Unit Tests (:pr:`3026`) +- Update Code Quality dependencies (:pr:`3070`, :pr:`3032`,:pr:`2998`, :pr:`2999`) +- Don't Set Signal Handlers On Windows By Default (:pr:`3065`) +- Split ``{Command, Prefix}Handler`` And Make Attributes Immutable (:pr:`3045`) +- Apply ``isort`` and Update ``pre-commit.ci`` Configuration (:pr:`3049`) +- Adjust ``pre-commit`` Settings for ``isort`` (:pr:`3043`) +- Add Version Check to Examples (:pr:`3036`) +- Use ``Collection`` Instead of ``List`` and ``Tuple`` (:pr:`3025`) +- Remove Client-Side Parameter Validation (:pr:`3024`) +- Don't Pass Default Values of Optional Parameters to Telegram (:pr:`2978`) +- Stabilize ``Application.run_*`` on Python 3.7 (:pr:`3009`) +- Ignore Code Style Commits in ``git blame`` (:pr:`3003`) +- Adjust Tests to Changed API Behavior (:pr:`3002`) + +Version 20.0a0 +============== +*Released 2022-05-06* + +This is the technical changelog for version 20.0a0. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes: +-------------- + +- Refactor Initialization of Persistence Classes + (:pr:`2604`) +- Drop Non-``CallbackContext`` API + (:pr:`2617`) +- Remove ``__dict__`` from ``__slots__`` and drop Python 3.6 + (:pr:`2619`, + :pr:`2636`) +- Move and Rename ``TelegramDecryptionError`` to + ``telegram.error.PassportDecryptionError`` + (:pr:`2621`) +- Make ``BasePersistence`` Methods Abstract + (:pr:`2624`) +- Remove ``day_is_strict`` argument of ``JobQueue.run_monthly`` + (:pr:`2634` + by `iota-008 `__) +- Move ``Defaults`` to ``telegram.ext`` + (:pr:`2648`) +- Remove Deprecated Functionality + (:pr:`2644`, + :pr:`2740`, + :pr:`2745`) +- Overhaul of Filters + (:pr:`2759`, + :pr:`2922`) +- Switch to ``asyncio`` and Refactor PTBs Architecture + (:pr:`2731`) +- Improve ``Job.__getattr__`` + (:pr:`2832`) +- Remove ``telegram.ReplyMarkup`` + (:pr:`2870`) +- Persistence of ``Bots``: Refactor Automatic Replacement and + Integration with ``TelegramObject`` + (:pr:`2893`) + +New Features: +------------- + +- Introduce Builder Pattern + (:pr:`2646`) +- Add ``Filters.update.edited`` + (:pr:`2705` + by `PhilippFr `__) +- Introduce ``Enums`` for ``telegram.constants`` + (:pr:`2708`) +- Accept File Paths for ``private_key`` + (:pr:`2724`) +- Associate ``Jobs`` with ``chat/user_id`` + (:pr:`2731`) +- Convenience Functionality for ``ChatInviteLinks`` + (:pr:`2782`) +- Add ``Dispatcher.add_handlers`` + (:pr:`2823`) +- Improve Error Messages in ``CommandHandler.__init__`` + (:pr:`2837`) +- ``Defaults.protect_content`` + (:pr:`2840`) +- Add ``Dispatcher.migrate_chat_data`` + (:pr:`2848` + by `DonalDuck004 `__) +- Add Method ``drop_chat/user_data`` to ``Dispatcher`` and Persistence + (:pr:`2852`) +- Add methods ``ChatPermissions.{all, no}_permissions`` (:pr:`2948`) +- Full Support for API 6.0 + (:pr:`2956`) +- Add Python 3.10 to Test Suite + (:pr:`2968`) + +Bug Fixes & Minor Changes: +-------------------------- + +- Improve Type Hinting for ``CallbackContext`` + (:pr:`2587` + by `revolter `__) +- Fix Signatures and Improve ``test_official`` + (:pr:`2643`) +- Refine ``Dispatcher.dispatch_error`` + (:pr:`2660`) +- Make ``InlineQuery.answer`` Raise ``ValueError`` + (:pr:`2675`) +- Improve Signature Inspection for Bot Methods + (:pr:`2686`) +- Introduce ``TelegramObject.set/get_bot`` + (:pr:`2712` + by `zpavloudis `__) +- Improve Subscription of ``TelegramObject`` + (:pr:`2719` + by `SimonDamberg `__) +- Use Enums for Dynamic Types & Rename Two Attributes in ``ChatMember`` + (:pr:`2817`) +- Return Plain Dicts from ``BasePersistence.get_*_data`` + (:pr:`2873`) +- Fix a Bug in ``ChatMemberUpdated.difference`` + (:pr:`2947`) +- Update Dependency Policy + (:pr:`2958`) + +Internal Restructurings & Improvements: +--------------------------------------- + +- Add User Friendly Type Check For Init Of + ``{Inline, Reply}KeyboardMarkup`` + (:pr:`2657`) +- Warnings Overhaul + (:pr:`2662`) +- Clear Up Import Policy + (:pr:`2671`) +- Mark Internal Modules As Private + (:pr:`2687` + by `kencx `__) +- Handle Filepaths via the ``pathlib`` Module + (:pr:`2688` + by `eldbud `__) +- Refactor MRO of ``InputMedia*`` and Some File-Like Classes + (:pr:`2717` + by `eldbud `__) +- Update Exceptions for Immutable Attributes + (:pr:`2749`) +- Refactor Warnings in ``ConversationHandler`` + (:pr:`2755`, + :pr:`2784`) +- Use ``__all__`` Consistently + (:pr:`2805`) + +CI, Code Quality & Test Suite Improvements: +------------------------------------------- + +- Add Custom ``pytest`` Marker to Ease Development + (:pr:`2628`) +- Pass Failing Jobs to Error Handlers + (:pr:`2692`) +- Update Notification Workflows + (:pr:`2695`) +- Use Error Messages for ``pylint`` Instead of Codes + (:pr:`2700` + by `Piraty `__) +- Make Tests Agnostic of the CWD + (:pr:`2727` + by `eldbud `__) +- Update Code Quality Dependencies + (:pr:`2748`) +- Improve Code Quality + (:pr:`2783`) +- Update ``pre-commit`` Settings & Improve a Test + (:pr:`2796`) +- Improve Code Quality & Test Suite + (:pr:`2843`) +- Fix failing animation tests + (:pr:`2865`) +- Update and Expand Tests & pre-commit Settings and Improve Code + Quality + (:pr:`2925`) +- Extend Code Formatting With Black + (:pr:`2972`) +- Update Workflow Permissions + (:pr:`2984`) +- Adapt Tests to Changed ``Bot.get_file`` Behavior + (:pr:`2995`) + +Documentation Improvements: +--------------------------- + +- Doc Fixes + (:pr:`2597`) +- Add Code Comment Guidelines to Contribution Guide + (:pr:`2612`) +- Add Cross-References to External Libraries & Other Documentation + Improvements + (:pr:`2693`, + :pr:`2691` + by `joesinghh `__, + :pr:`2739` + by `eldbud `__) +- Use Furo Theme, Make Parameters Referenceable, Add Documentation + Building to CI, Improve Links to Source Code & Other Improvements + (:pr:`2856`, + :pr:`2798`, + :pr:`2854`, + :pr:`2841`) +- Documentation Fixes & Improvements + (:pr:`2822`) +- Replace ``git.io`` Links + (:pr:`2872` + by `murugu-21 `__) +- Overhaul Readmes, Update RTD Startpage & Other Improvements + (:pr:`2969`) + +Version 13.11 +============= +*Released 2022-02-02* + +This is the technical changelog for version 13.11. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +**Major Changes:** + +- Full Support for Bot API 5.7 (:pr:`2881`) + +Version 13.10 +============= +*Released 2022-01-03* + +This is the technical changelog for version 13.10. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +**Major Changes:** + +- Full Support for API 5.6 (:pr:`2835`) + +**Minor Changes & Doc fixes:** + +- Update Copyright to 2022 (:pr:`2836`) +- Update Documentation of ``BotCommand`` (:pr:`2820`) + +Version 13.9 +============ +*Released 2021-12-11* + +This is the technical changelog for version 13.9. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +**Major Changes:** + +- Full Support for Api 5.5 (:pr:`2809`) + +**Minor Changes** + +- Adjust Automated Locking of Inactive Issues (:pr:`2775`) + +Version 13.8.1 +============== +*Released 2021-11-08* + +This is the technical changelog for version 13.8.1. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +**Doc fixes:** + +- Add ``ChatJoinRequest(Handler)`` to Docs (:pr:`2771`) + +Version 13.8 +============ +*Released 2021-11-08* + +This is the technical changelog for version 13.8. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +**Major Changes:** + +- Full support for API 5.4 (:pr:`2767`) + +**Minor changes, CI improvements, Doc fixes and Type hinting:** + +- Create Issue Template Forms (:pr:`2689`) +- Fix ``camelCase`` Functions in ``ExtBot`` (:pr:`2659`) +- Fix Empty Captions not Being Passed by ``Bot.copy_message`` (:pr:`2651`) +- Fix Setting Thumbs When Uploading A Single File (:pr:`2583`) +- Fix Bug in ``BasePersistence.insert``/``replace_bot`` for Objects with ``__dict__`` not in ``__slots__`` (:pr:`2603`) + +Version 13.7 +============ +*Released 2021-07-01* + +This is the technical changelog for version 13.7. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +**Major Changes:** + +- Full support for Bot API 5.3 (:pr:`2572`) + +**Bug Fixes:** + +- Fix Bug in ``BasePersistence.insert/replace_bot`` for Objects with ``__dict__`` in their slots (:pr:`2561`) +- Remove Incorrect Warning About ``Defaults`` and ``ExtBot`` (:pr:`2553`) + +**Minor changes, CI improvements, Doc fixes and Type hinting:** + +- Type Hinting Fixes (:pr:`2552`) +- Doc Fixes (:pr:`2551`) +- Improve Deprecation Warning for ``__slots__`` (:pr:`2574`) +- Stabilize CI (:pr:`2575`) +- Fix Coverage Configuration (:pr:`2571`) +- Better Exception-Handling for ``BasePersistence.replace/insert_bot`` (:pr:`2564`) +- Remove Deprecated ``pass_args`` from Deeplinking Example (:pr:`2550`) + +Version 13.6 +============ +*Released 2021-06-06* + +New Features: + +- Arbitrary ``callback_data`` (:pr:`1844`) +- Add ``ContextTypes`` & ``BasePersistence.refresh_user/chat/bot_data`` (:pr:`2262`) +- Add ``Filters.attachment`` (:pr:`2528`) +- Add ``pattern`` Argument to ``ChosenInlineResultHandler`` (:pr:`2517`) + +Major Changes: + +- Add ``slots`` (:pr:`2345`) + +Minor changes, CI improvements, Doc fixes and Type hinting: + +- Doc Fixes (:pr:`2495`, :pr:`2510`) +- Add ``max_connections`` Parameter to ``Updater.start_webhook`` (:pr:`2547`) +- Fix for ``Promise.done_callback`` (:pr:`2544`) +- Improve Code Quality (:pr:`2536`, :pr:`2454`) +- Increase Test Coverage of ``CallbackQueryHandler`` (:pr:`2520`) +- Stabilize CI (:pr:`2522`, :pr:`2537`, :pr:`2541`) +- Fix ``send_phone_number_to_provider`` argument for ``Bot.send_invoice`` (:pr:`2527`) +- Handle Classes as Input for ``BasePersistence.replace/insert_bot`` (:pr:`2523`) +- Bump Tornado Version and Remove Workaround from :pr:`2067` (:pr:`2494`) + +Version 13.5 +============ +*Released 2021-04-30* + +**Major Changes:** + +- Full support of Bot API 5.2 (:pr:`2489`). + + .. note:: + The ``start_parameter`` argument of ``Bot.send_invoice`` and the corresponding shortcuts is now optional, so the order of + parameters had to be changed. Make sure to update your method calls accordingly. + +- Update ``ChatActions``, Deprecating ``ChatAction.RECORD_AUDIO`` and ``ChatAction.UPLOAD_AUDIO`` (:pr:`2460`) + +**New Features:** + +- Convenience Utilities & Example for Handling ``ChatMemberUpdated`` (:pr:`2490`) +- ``Filters.forwarded_from`` (:pr:`2446`) + +**Minor changes, CI improvements, Doc fixes and Type hinting:** + +- Improve Timeouts in ``ConversationHandler`` (:pr:`2417`) +- Stabilize CI (:pr:`2480`) +- Doc Fixes (:pr:`2437`) +- Improve Type Hints of Data Filters (:pr:`2456`) +- Add Two ``UserWarnings`` (:pr:`2464`) +- Improve Code Quality (:pr:`2450`) +- Update Fallback Test-Bots (:pr:`2451`) +- Improve Examples (:pr:`2441`, :pr:`2448`) + +Version 13.4.1 +============== +*Released 2021-03-14* + +**Hot fix release:** + +- Fixed a bug in ``setup.py`` (:pr:`2431`) + +Version 13.4 +============ +*Released 2021-03-14* + +**Major Changes:** + +- Full support of Bot API 5.1 (:pr:`2424`) + +**Minor changes, CI improvements, doc fixes and type hinting:** + +- Improve ``Updater.set_webhook`` (:pr:`2419`) +- Doc Fixes (:pr:`2404`) +- Type Hinting Fixes (:pr:`2425`) +- Update ``pre-commit`` Settings (:pr:`2415`) +- Fix Logging for Vendored ``urllib3`` (:pr:`2427`) +- Stabilize Tests (:pr:`2409`) + +Version 13.3 +============ +*Released 2021-02-19* + +**Major Changes:** + +- Make ``cryptography`` Dependency Optional & Refactor Some Tests (:pr:`2386`, :pr:`2370`) +- Deprecate ``MessageQueue`` (:pr:`2393`) + +**Bug Fixes:** + +- Refactor ``Defaults`` Integration (:pr:`2363`) +- Add Missing ``telegram.SecureValue`` to init and Docs (:pr:`2398`) + +**Minor changes:** + +- Doc Fixes (:pr:`2359`) + +Version 13.2 +============ +*Released 2021-02-02* + +**Major Changes:** + +- Introduce ``python-telegram-bot-raw`` (:pr:`2324`) +- Explicit Signatures for Shortcuts (:pr:`2240`) + +**New Features:** + +- Add Missing Shortcuts to ``Message`` (:pr:`2330`) +- Rich Comparison for ``Bot`` (:pr:`2320`) +- Add ``run_async`` Parameter to ``ConversationHandler`` (:pr:`2292`) +- Add New Shortcuts to ``Chat`` (:pr:`2291`) +- Add New Constant ``MAX_ANSWER_CALLBACK_QUERY_TEXT_LENGTH`` (:pr:`2282`) +- Allow Passing Custom Filename For All Media (:pr:`2249`) +- Handle Bytes as File Input (:pr:`2233`) + +**Bug Fixes:** + +- Fix Escaping in Nested Entities in ``Message`` Properties (:pr:`2312`) +- Adjust Calling of ``Dispatcher.update_persistence`` (:pr:`2285`) +- Add ``quote`` kwarg to ``Message.reply_copy`` (:pr:`2232`) +- ``ConversationHandler``: Docs & ``edited_channel_post`` behavior (:pr:`2339`) + +**Minor changes, CI improvements, doc fixes and type hinting:** + +- Doc Fixes (:pr:`2253`, :pr:`2225`) +- Reduce Usage of ``typing.Any`` (:pr:`2321`) +- Extend Deeplinking Example (:pr:`2335`) +- Add pyupgrade to pre-commit Hooks (:pr:`2301`) +- Add PR Template (:pr:`2299`) +- Drop Nightly Tests & Update Badges (:pr:`2323`) +- Update Copyright (:pr:`2289`, :pr:`2287`) +- Change Order of Class DocStrings (:pr:`2256`) +- Add macOS to Test Matrix (:pr:`2266`) +- Start Using Versioning Directives in Docs (:pr:`2252`) +- Improve Annotations & Docs of Handlers (:pr:`2243`) + +Version 13.1 +============ +*Released 2020-11-29* + +**Major Changes:** + +- Full support of Bot API 5.0 (:pr:`2181`, :pr:`2186`, :pr:`2190`, :pr:`2189`, :pr:`2183`, :pr:`2184`, :pr:`2188`, :pr:`2185`, :pr:`2192`, :pr:`2196`, :pr:`2193`, :pr:`2223`, :pr:`2199`, :pr:`2187`, :pr:`2147`, :pr:`2205`) + +**New Features:** + +- Add ``Defaults.run_async`` (:pr:`2210`) +- Improve and Expand ``CallbackQuery`` Shortcuts (:pr:`2172`) +- Add XOR Filters and make ``Filters.name`` a Property (:pr:`2179`) +- Add ``Filters.document.file_extension`` (:pr:`2169`) +- Add ``Filters.caption_regex`` (:pr:`2163`) +- Add ``Filters.chat_type`` (:pr:`2128`) +- Handle Non-Binary File Input (:pr:`2202`) + +**Bug Fixes:** + +- Improve Handling of Custom Objects in ``BasePersistence.insert``/``replace_bot`` (:pr:`2151`) +- Fix bugs in ``replace/insert_bot`` (:pr:`2218`) + +**Minor changes, CI improvements, doc fixes and type hinting:** + +- Improve Type hinting (:pr:`2204`, :pr:`2118`, :pr:`2167`, :pr:`2136`) +- Doc Fixes & Extensions (:pr:`2201`, :pr:`2161`) +- Use F-Strings Where Possible (:pr:`2222`) +- Rename kwargs to _kwargs where possible (:pr:`2182`) +- Comply with PEP561 (:pr:`2168`) +- Improve Code Quality (:pr:`2131`) +- Switch Code Formatting to Black (:pr:`2122`, :pr:`2159`, :pr:`2158`) +- Update Wheel Settings (:pr:`2142`) +- Update ``timerbot.py`` to ``v13.0`` (:pr:`2149`) +- Overhaul Constants (:pr:`2137`) +- Add Python 3.9 to Test Matrix (:pr:`2132`) +- Switch Codecov to ``GitHub`` Action (:pr:`2127`) +- Specify Required pytz Version (:pr:`2121`) + +Version 13.0 +============ +*Released 2020-10-07* + +**For a detailed guide on how to migrate from v12 to v13, see this** `wiki page `_. + +**Major Changes:** + +- Deprecate old-style callbacks, i.e. set ``use_context=True`` by default (:pr:`2050`) +- Refactor Handling of Message VS Update Filters (:pr:`2032`) +- Deprecate ``Message.default_quote`` (:pr:`1965`) +- Refactor persistence of Bot instances (:pr:`1994`) +- Refactor ``JobQueue`` (:pr:`1981`) +- Refactor handling of kwargs in Bot methods (:pr:`1924`) +- Refactor ``Dispatcher.run_async``, deprecating the ``@run_async`` decorator (:pr:`2051`) + +**New Features:** + +- Type Hinting (:pr:`1920`) +- Automatic Pagination for ``answer_inline_query`` (:pr:`2072`) +- ``Defaults.tzinfo`` (:pr:`2042`) +- Extend rich comparison of objects (:pr:`1724`) +- Add ``Filters.via_bot`` (:pr:`2009`) +- Add missing shortcuts (:pr:`2043`) +- Allow ``DispatcherHandlerStop`` in ``ConversationHandler`` (:pr:`2059`) +- Make Errors picklable (:pr:`2106`) + +**Minor changes, CI improvements, doc fixes or bug fixes:** + +- Fix Webhook not working on Windows with Python 3.8+ (:pr:`2067`) +- Fix setting thumbs with ``send_media_group`` (:pr:`2093`) +- Make ``MessageHandler`` filter for ``Filters.update`` first (:pr:`2085`) +- Fix ``PicklePersistence.flush()`` with only ``bot_data`` (:pr:`2017`) +- Add test for clean argument of ``Updater.start_polling/webhook`` (:pr:`2002`) +- Doc fixes, refinements and additions (:pr:`2005`, :pr:`2008`, :pr:`2089`, :pr:`2094`, :pr:`2090`) +- CI fixes (:pr:`2018`, :pr:`2061`) +- Refine ``pollbot.py`` example (:pr:`2047`) +- Refine Filters in examples (:pr:`2027`) +- Rename ``echobot`` examples (:pr:`2025`) +- Use Lock-Bot to lock old threads (:pr:`2048`, :pr:`2052`, :pr:`2049`, :pr:`2053`) + +Version 12.8 +============ +*Released 2020-06-22* + +**Major Changes:** + +- Remove Python 2 support (:pr:`1715`) +- Bot API 4.9 support (:pr:`1980`) +- IDs/Usernames of ``Filters.user`` and ``Filters.chat`` can now be updated (:pr:`1757`) + +**Minor changes, CI improvements, doc fixes or bug fixes:** + +- Update contribution guide and stale bot (:pr:`1937`) +- Remove ``NullHandlers`` (:pr:`1913`) +- Improve and expand examples (:pr:`1943`, :pr:`1995`, :pr:`1983`, :pr:`1997`) +- Doc fixes (:pr:`1940`, :pr:`1962`) +- Add ``User.send_poll()`` shortcut (:pr:`1968`) +- Ignore private attributes en ``TelegramObject.to_dict()`` (:pr:`1989`) +- Stabilize CI (:pr:`2000`) + +Version 12.7 +============ +*Released 2020-05-02* + +**Major Changes:** + +- Bot API 4.8 support. **Note:** The ``Dice`` object now has a second positional argument ``emoji``. This is relevant, if you instantiate ``Dice`` objects manually. (:pr:`1917`) +- Added ``tzinfo`` argument to ``helpers.from_timestamp``. It now returns an timezone aware object. This is relevant for ``Message.{date,forward_date,edit_date}``, ``Poll.close_date`` and ``ChatMember.until_date`` (:pr:`1621`) + +**New Features:** + +- New method ``run_monthly`` for the ``JobQueue`` (:pr:`1705`) +- ``Job.next_t`` now gives the datetime of the jobs next execution (:pr:`1685`) + +**Minor changes, CI improvements, doc fixes or bug fixes:** + +- Stabalize CI (:pr:`1919`, :pr:`1931`) +- Use ABCs ``@abstractmethod`` instead of raising ``NotImplementedError`` for ``Handler``, ``BasePersistence`` and ``BaseFilter`` (:pr:`1905`) +- Doc fixes (:pr:`1914`, :pr:`1902`, :pr:`1910`) + +Version 12.6.1 +============== +*Released 2020-04-11* + +**Bug fixes:** + +- Fix serialization of ``reply_markup`` in media messages (:pr:`1889`) + +Version 12.6 +============ +*Released 2020-04-10* + +**Major Changes:** + +- Bot API 4.7 support. **Note:** In ``Bot.create_new_sticker_set`` and ``Bot.add_sticker_to_set``, the order of the parameters had be changed, as the ``png_sticker`` parameter is now optional. (:pr:`1858`) + +**Minor changes, CI improvements or bug fixes:** + +- Add tests for ``swtich_inline_query(_current_chat)`` with empty string (:pr:`1635`) +- Doc fixes (:pr:`1854`, :pr:`1874`, :pr:`1884`) +- Update issue templates (:pr:`1880`) +- Favor concrete types over "Iterable" (:pr:`1882`) +- Pass last valid ``CallbackContext`` to ``TIMEOUT`` handlers of ``ConversationHandler`` (:pr:`1826`) +- Tweak handling of persistence and update persistence after job calls (:pr:`1827`) +- Use checkout@v2 for GitHub actions (:pr:`1887`) + +Version 12.5.1 +============== +*Released 2020-03-30* + +**Minor changes, doc fixes or bug fixes:** + +- Add missing docs for `PollHandler` and `PollAnswerHandler` (:pr:`1853`) +- Fix wording in `Filters` docs (:pr:`1855`) +- Reorder tests to make them more stable (:pr:`1835`) +- Make `ConversationHandler` attributes immutable (:pr:`1756`) +- Make `PrefixHandler` attributes `command` and `prefix` editable (:pr:`1636`) +- Fix UTC as default `tzinfo` for `Job` (:pr:`1696`) + +Version 12.5 +============ +*Released 2020-03-29* + +**New Features:** + +- `Bot.link` gives the `t.me` link of the bot (:pr:`1770`) + +**Major Changes:** + +- Bot API 4.5 and 4.6 support. (:pr:`1508`, :pr:`1723`) + +**Minor changes, CI improvements or bug fixes:** + +- Remove legacy CI files (:pr:`1783`, :pr:`1791`) +- Update pre-commit config file (:pr:`1787`) +- Remove builtin names (:pr:`1792`) +- CI improvements (:pr:`1808`, :pr:`1848`) +- Support Python 3.8 (:pr:`1614`, :pr:`1824`) +- Use stale bot for auto closing stale issues (:pr:`1820`, :pr:`1829`, :pr:`1840`) +- Doc fixes (:pr:`1778`, :pr:`1818`) +- Fix typo in `edit_message_media` (:pr:`1779`) +- In examples, answer CallbackQueries and use `edit_message_text` shortcut (:pr:`1721`) +- Revert accidental change in vendored urllib3 (:pr:`1775`) + +Version 12.4.2 +============== +*Released 2020-02-10* + +**Bug Fixes** + +- Pass correct parse_mode to InlineResults if bot.defaults is None (:pr:`1763`) +- Make sure PP can read files that dont have bot_data (:pr:`1760`) + +Version 12.4.1 +============== +*Released 2020-02-08* + +This is a quick release for :pr:`1744` which was accidently left out of v12.4.0 though mentioned in the +release notes. + +Version 12.4.0 +============== +*Released 2020-02-08* + +**New features:** + +- Set default values for arguments appearing repeatedly. We also have a `wiki page for the new defaults`_. (:pr:`1490`) +- Store data in ``CallbackContext.bot_data`` to access it in every callback. Also persists. (:pr:`1325`) +- ``Filters.poll`` allows only messages containing a poll (:pr:`1673`) + +**Major changes:** + +- ``Filters.text`` now accepts messages that start with a slash, because ``CommandHandler`` checks for ``MessageEntity.BOT_COMMAND`` since v12. This might lead to your MessageHandlers receiving more updates than before (:pr:`1680`). +- ``Filters.command`` new checks for ``MessageEntity.BOT_COMMAND`` instead of just a leading slash. Also by ``Filters.command(False)`` you can now filters for messages containing a command `anywhere` in the text (:pr:`1744`). + +**Minor changes, CI improvements or bug fixes:** + +- Add ``disptacher`` argument to ``Updater`` to allow passing a customized ``Dispatcher`` (:pr:`1484`) +- Add missing names for ``Filters`` (:pr:`1632`) +- Documentation fixes (:pr:`1624`, :pr:`1647`, :pr:`1669`, :pr:`1703`, :pr:`1718`, :pr:`1734`, :pr:`1740`, :pr:`1642`, :pr:`1739`, :pr:`1746`) +- CI improvements (:pr:`1716`, :pr:`1731`, :pr:`1738`, :pr:`1748`, :pr:`1749`, :pr:`1750`, :pr:`1752`) +- Fix spelling issue for ``encode_conversations_to_json`` (:pr:`1661`) +- Remove double assignement of ``Dispatcher.job_queue`` (:pr:`1698`) +- Expose dispatcher as property for ``CallbackContext`` (:pr:`1684`) +- Fix ``None`` check in ``JobQueue._put()`` (:pr:`1707`) +- Log datetimes correctly in ``JobQueue`` (:pr:`1714`) +- Fix false ``Message.link`` creation for private groups (:pr:`1741`) +- Add option ``--with-upstream-urllib3`` to `setup.py` to allow using non-vendored version (:pr:`1725`) +- Fix persistence for nested ``ConversationHandlers`` (:pr:`1679`) +- Improve handling of non-decodable server responses (:pr:`1623`) +- Fix download for files without ``file_path`` (:pr:`1591`) +- test_webhook_invalid_posts is now considered flaky and retried on failure (:pr:`1758`) + +.. _`wiki page for the new defaults`: https://github.com/python-telegram-bot/python-telegram-bot/wiki/Adding-defaults-to-your-bot + +Version 12.3.0 +============== +*Released 2020-01-11* + +**New features:** + +- `Filters.caption` allows only messages with caption (:pr:`1631`). +- Filter for exact messages/captions with new capability of `Filters.text` and `Filters.caption`. Especially useful in combination with ReplyKeyboardMarkup. (:pr:`1631`). + +**Major changes:** + +- Fix inconsistent handling of naive datetimes (:pr:`1506`). + +**Minor changes, CI improvements or bug fixes:** + +- Documentation fixes (:pr:`1558`, :pr:`1569`, :pr:`1579`, :pr:`1572`, :pr:`1566`, :pr:`1577`, :pr:`1656`). +- Add mutex protection on `ConversationHandler` (:pr:`1533`). +- Add `MAX_PHOTOSIZE_UPLOAD` constant (:pr:`1560`). +- Add args and kwargs to `Message.forward()` (:pr:`1574`). +- Transfer to GitHub Actions CI (:pr:`1555`, :pr:`1556`, :pr:`1605`, :pr:`1606`, :pr:`1607`, :pr:`1612`, :pr:`1615`, :pr:`1645`). +- Fix deprecation warning with Py3.8 by vendored urllib3 (:pr:`1618`). +- Simplify assignements for optional arguments (:pr:`1600`) +- Allow private groups for `Message.link` (:pr:`1619`). +- Fix wrong signature call for `ConversationHandler.TIMEOUT` handlers (:pr:`1653`). + +Version 12.2.0 +============== +*Released 2019-10-14* + +**New features:** + +- Nested ConversationHandlers (:pr:`1512`). + +**Minor changes, CI improvments or bug fixes:** + +- Fix CI failures due to non-backward compat attrs depndency (:pr:`1540`). +- travis.yaml: TEST_OFFICIAL removed from allowed_failures. +- Fix typos in examples (:pr:`1537`). +- Fix Bot.to_dict to use proper first_name (:pr:`1525`). +- Refactor ``test_commandhandler.py`` (:pr:`1408`). +- Add Python 3.8 (RC version) to Travis testing matrix (:pr:`1543`). +- test_bot.py: Add to_dict test (:pr:`1544`). +- Flake config moved into setup.cfg (:pr:`1546`). + +Version 12.1.1 +============== +*Released 2019-09-18* + +**Hot fix release** + +Fixed regression in the vendored urllib3 (:pr:`1517`). + +Version 12.1.0 +================ +*Released 2019-09-13* + +**Major changes:** + +- Bot API 4.4 support (:pr:`1464`, :pr:`1510`) +- Add `get_file` method to `Animation` & `ChatPhoto`. Add, `get_small_file` & `get_big_file` + methods to `ChatPhoto` (:pr:`1489`) +- Tools for deep linking (:pr:`1049`) + +**Minor changes and/or bug fixes:** + +- Documentation fixes (:pr:`1500`, :pr:`1499`) +- Improved examples (:pr:`1502`) + +Version 12.0.0 +================ +*Released 2019-08-29* + +Well... This felt like decades. But here we are with a new release. + +Expect minor releases soon (mainly complete Bot API 4.4 support) + +**Major and/or breaking changes:** + +- Context based callbacks +- Persistence +- PrefixHandler added (Handler overhaul) +- Deprecation of RegexHandler and edited_messages, channel_post, etc. arguments (Filter overhaul) +- Various ConversationHandler changes and fixes +- Bot API 4.1, 4.2, 4.3 support +- Python 3.4 is no longer supported +- Error Handler now handles all types of exceptions (:pr:`1485`) +- Return UTC from from_timestamp() (:pr:`1485`) + +**See the wiki page at https://github.com/python-telegram-bot/python-telegram-bot/wiki/Transition-guide-to-Version-12.0 for a detailed guide on how to migrate from version 11 to version 12.** + +Context based callbacks (:pr:`1100`) +------------------------------------ + +- Use of ``pass_`` in handlers is deprecated. +- Instead use ``use_context=True`` on ``Updater`` or ``Dispatcher`` and change callback from (bot, update, others...) to (update, context). +- This also applies to error handlers ``Dispatcher.add_error_handler`` and JobQueue jobs (change (bot, job) to (context) here). +- For users with custom handlers subclassing Handler, this is mostly backwards compatible, but to use the new context based callbacks you need to implement the new collect_additional_context method. +- Passing bot to ``JobQueue.__init__`` is deprecated. Use JobQueue.set_dispatcher with a dispatcher instead. +- Dispatcher makes sure to use a single `CallbackContext` for a entire update. This means that if an update is handled by multiple handlers (by using the group argument), you can add custom arguments to the `CallbackContext` in a lower group handler and use it in higher group handler. NOTE: Never use with @run_async, see docs for more info. (:pr:`1283`) +- If you have custom handlers they will need to be updated to support the changes in this release. +- Update all examples to use context based callbacks. + +Persistence (:pr:`1017`) +------------------------ + +- Added PicklePersistence and DictPersistence for adding persistence to your bots. +- BasePersistence can be subclassed for all your persistence needs. +- Add a new example that shows a persistent ConversationHandler bot + +Handler overhaul (:pr:`1114`) +----------------------------- + +- CommandHandler now only triggers on actual commands as defined by telegram servers (everything that the clients mark as a tabable link). +- PrefixHandler can be used if you need to trigger on prefixes (like all messages starting with a "/" (old CommandHandler behaviour) or even custom prefixes like "#" or "!"). + +Filter overhaul (:pr:`1221`) +---------------------------- + +- RegexHandler is deprecated and should be replaced with a MessageHandler with a regex filter. +- Use update filters to filter update types instead of arguments (message_updates, channel_post_updates and edited_updates) on the handlers. +- Completely remove allow_edited argument - it has been deprecated for a while. +- data_filters now exist which allows filters that return data into the callback function. This is how the regex filter is implemented. +- All this means that it no longer possible to use a list of filters in a handler. Use bitwise operators instead! + +ConversationHandler +------------------- + +- Remove ``run_async_timeout`` and ``timed_out_behavior`` arguments (:pr:`1344`) +- Replace with ``WAITING`` constant and behavior from states (:pr:`1344`) +- Only emit one warning for multiple CallbackQueryHandlers in a ConversationHandler (:pr:`1319`) +- Use warnings.warn for ConversationHandler warnings (:pr:`1343`) +- Fix unresolvable promises (:pr:`1270`) + +Bug fixes & improvements +------------------------ + +- Handlers should be faster due to deduped logic. +- Avoid compiling compiled regex in regex filter. (:pr:`1314`) +- Add missing ``left_chat_member`` to Message.MESSAGE_TYPES (:pr:`1336`) +- Make custom timeouts actually work properly (:pr:`1330`) +- Add convenience classmethods (from_button, from_row and from_column) to InlineKeyboardMarkup +- Small typo fix in setup.py (:pr:`1306`) +- Add Conflict error (HTTP error code 409) (:pr:`1154`) +- Change MAX_CAPTION_LENGTH to 1024 (:pr:`1262`) +- Remove some unnecessary clauses (:pr:`1247`, :pr:`1239`) +- Allow filenames without dots in them when sending files (:pr:`1228`) +- Fix uploading files with unicode filenames (:pr:`1214`) +- Replace http.server with Tornado (:pr:`1191`) +- Allow SOCKSConnection to parse username and password from URL (:pr:`1211`) +- Fix for arguments in passport/data.py (:pr:`1213`) +- Improve message entity parsing by adding text_mention (:pr:`1206`) +- Documentation fixes (:pr:`1348`, :pr:`1397`, :pr:`1436`) +- Merged filters short-circuit (:pr:`1350`) +- Fix webhook listen with tornado (:pr:`1383`) +- Call task_done() on update queue after update processing finished (:pr:`1428`) +- Fix send_location() - latitude may be 0 (:pr:`1437`) +- Make MessageEntity objects comparable (:pr:`1465`) +- Add prefix to thread names (:pr:`1358`) + +Buf fixes since v12.0.0b1 +------------------------- + +- Fix setting bot on ShippingQuery (:pr:`1355`) +- Fix _trigger_timeout() missing 1 required positional argument: 'job' (:pr:`1367`) +- Add missing message.text check in PrefixHandler check_update (:pr:`1375`) +- Make updates persist even on DispatcherHandlerStop (:pr:`1463`) +- Dispatcher force updating persistence object's chat data attribute(:pr:`1462`) + +Internal improvements +--------------------- + +- Finally fix our CI builds mostly (too many commits and PRs to list) +- Use multiple bots for CI to improve testing times significantly. +- Allow pypy to fail in CI. +- Remove the last CamelCase CheckUpdate methods from the handlers we missed earlier. +- test_official is now executed in a different job + +Version 11.1.0 +============== +*Released 2018-09-01* + +Fixes and updates for Telegram Passport: (:pr:`1198`) + +- Fix passport decryption failing at random times +- Added support for middle names. +- Added support for translations for documents +- Add errors for translations for documents +- Added support for requesting names in the language of the user's country of residence +- Replaced the payload parameter with the new parameter nonce +- Add hash to EncryptedPassportElement + +Version 11.0.0 +============== +*Released 2018-08-29* + +Fully support Bot API version 4.0! +(also some bugfixes :)) + +Telegram Passport (:pr:`1174`): + +- Add full support for telegram passport. + - New types: PassportData, PassportFile, EncryptedPassportElement, EncryptedCredentials, PassportElementError, PassportElementErrorDataField, PassportElementErrorFrontSide, PassportElementErrorReverseSide, PassportElementErrorSelfie, PassportElementErrorFile and PassportElementErrorFiles. + - New bot method: set_passport_data_errors + - New filter: Filters.passport_data + - Field passport_data field on Message + - PassportData can be easily decrypted. + - PassportFiles are automatically decrypted if originating from decrypted PassportData. +- See new passportbot.py example for details on how to use, or go to `our telegram passport wiki page`_ for more info +- NOTE: Passport decryption requires new dependency `cryptography`. + +Inputfile rework (:pr:`1184`): + +- Change how Inputfile is handled internally +- This allows support for specifying the thumbnails of photos and videos using the thumb= argument in the different send\_ methods. +- Also allows Bot.send_media_group to actually finally send more than one media. +- Add thumb to Audio, Video and Videonote +- Add Bot.edit_message_media together with InputMediaAnimation, InputMediaAudio, and inputMediaDocument. + +Other Bot API 4.0 changes: + +- Add forusquare_type to Venue, InlineQueryResultVenue, InputVenueMessageContent, and Bot.send_venue. (:pr:`1170`) +- Add vCard support by adding vcard field to Contact, InlineQueryResultContact, InputContactMessageContent, and Bot.send_contact. (:pr:`1166`) +- Support new message entities: CASHTAG and PHONE_NUMBER. (:pr:`1179`) + - Cashtag seems to be things like `$USD` and `$GBP`, but it seems telegram doesn't currently send them to bots. + - Phone number also seems to have limited support for now +- Add Bot.send_animation, add width, height, and duration to Animation, and add Filters.animation. (:pr:`1172`) + +Non Bot API 4.0 changes: + +- Minor integer comparison fix (:pr:`1147`) +- Fix Filters.regex failing on non-text message (:pr:`1158`) +- Fix ProcessLookupError if process finishes before we kill it (:pr:`1126`) +- Add t.me links for User, Chat and Message if available and update User.mention_* (:pr:`1092`) +- Fix mention_markdown/html on py2 (:pr:`1112`) + +.. _`our telegram passport wiki page`: https://github.com/python-telegram-bot/python-telegram-bot/wiki/Telegram-Passport + +Version 10.1.0 +============== +*Released 2018-05-02* + +Fixes changing previous behaviour: + +- Add urllib3 fix for socks5h support (:pr:`1085`) +- Fix send_sticker() timeout=20 (:pr:`1088`) + +Fixes: + +- Add a caption_entity filter for filtering caption entities (:pr:`1068`) +- Inputfile encode filenames (:pr:`1086`) +- InputFile: Fix proper naming of file when reading from subprocess.PIPE (:pr:`1079`) +- Remove pytest-catchlog from requirements (:pr:`1099`) +- Documentation fixes (:pr:`1061`, :pr:`1078`, :pr:`1081`, :pr:`1096`) + +Version 10.0.2 +============== +*Released 2018-04-17* + +Important fix: + +- Handle utf8 decoding errors (:pr:`1076`) + +New features: + +- Added Filter.regex (:pr:`1028`) +- Filters for Category and file types (:pr:`1046`) +- Added video note filter (:pr:`1067`) + +Fixes: + +- Fix in telegram.Message (:pr:`1042`) +- Make chat_id a positional argument inside shortcut methods of Chat and User classes (:pr:`1050`) +- Make Bot.full_name return a unicode object. (:pr:`1063`) +- CommandHandler faster check (:pr:`1074`) +- Correct documentation of Dispatcher.add_handler (:pr:`1071`) +- Various small fixes to documentation. + +Version 10.0.1 +============== +*Released 2018-03-05* + +Fixes: + +- Fix conversationhandler timeout (PR :pr:`1032`) +- Add missing docs utils (PR :pr:`912`) + +Version 10.0.0 +============== +*Released 2018-03-02* + +Non backward compatabile changes and changed defaults + +- JobQueue: Remove deprecated prevent_autostart & put() (PR :pr:`1012`) +- Bot, Updater: Remove deprecated network_delay (PR :pr:`1012`) +- Remove deprecated Message.new_chat_member (PR :pr:`1012`) +- Retry bootstrap phase indefinitely (by default) on network errors (PR :pr:`1018`) + +New Features + +- Support v3.6 API (PR :pr:`1006`) +- User.full_name convinience property (PR :pr:`949`) +- Add `send_phone_number_to_provider` and `send_email_to_provider` arguments to send_invoice (PR :pr:`986`) +- Bot: Add shortcut methods reply_{markdown,html} (PR :pr:`827`) +- Bot: Add shortcut method reply_media_group (PR :pr:`994`) +- Added utils.helpers.effective_message_type (PR :pr:`826`) +- Bot.get_file now allows passing a file in addition to file_id (PR :pr:`963`) +- Add .get_file() to Audio, Document, PhotoSize, Sticker, Video, VideoNote and Voice (PR :pr:`963`) +- Add .send_*() methods to User and Chat (PR :pr:`963`) +- Get jobs by name (PR :pr:`1011`) +- Add Message caption html/markdown methods (PR :pr:`1013`) +- File.download_as_bytearray - new method to get a d/led file as bytearray (PR :pr:`1019`) +- File.download(): Now returns a meaningful return value (PR :pr:`1019`) +- Added conversation timeout in ConversationHandler (PR :pr:`895`) + +Changes + +- Store bot in PreCheckoutQuery (PR :pr:`953`) +- Updater: Issue INFO log upon received signal (PR :pr:`951`) +- JobQueue: Thread safety fixes (PR :pr:`977`) +- WebhookHandler: Fix exception thrown during error handling (PR :pr:`985`) +- Explicitly check update.effective_chat in ConversationHandler.check_update (PR :pr:`959`) +- Updater: Better handling of timeouts during get_updates (PR :pr:`1007`) +- Remove unnecessary to_dict() (PR :pr:`834`) +- CommandHandler - ignore strings in entities and "/" followed by whitespace (PR :pr:`1020`) +- Documentation & style fixes (PR :pr:`942`, PR :pr:`956`, PR :pr:`962`, PR :pr:`980`, PR :pr:`983`) + +Version 9.0.0 +============= +*Released 2017-12-08* + +Breaking changes (possibly) + +- Drop support for python 3.3 (PR :pr:`930`) + +New Features + +- Support Bot API 3.5 (PR :pr:`920`) + +Changes + +- Fix race condition in dispatcher start/stop (:pr:`887`) +- Log error trace if there is no error handler registered (:pr:`694`) +- Update examples with consistent string formatting (:pr:`870`) +- Various changes and improvements to the docs. + +Version 8.1.1 +============= +*Released 2017-10-15* + +- Fix Commandhandler crashing on single character messages (PR :pr:`873`). + +Version 8.1.0 +============= +*Released 2017-10-14* + +New features +- Support Bot API 3.4 (PR :pr:`865`). + +Changes +- MessageHandler & RegexHandler now consider channel_updates. +- Fix command not recognized if it is directly followed by a newline (PR :pr:`869`). +- Removed Bot._message_wrapper (PR :pr:`822`). +- Unitests are now also running on AppVeyor (Windows VM). +- Various unitest improvements. +- Documentation fixes. + +Version 8.0.0 +============= +*Released 2017-09-01* + +New features + +- Fully support Bot Api 3.3 (PR :pr:`806`). +- DispatcherHandlerStop (`see docs`_). +- Regression fix for text_html & text_markdown (PR :pr:`777`). +- Added effective_attachment to message (PR :pr:`766`). + +Non backward compatible changes + +- Removed Botan support from the library (PR :pr:`776`). +- Fully support Bot Api 3.3 (PR :pr:`806`). +- Remove de_json() (PR :pr:`789`). + +Changes + +- Sane defaults for tcp socket options on linux (PR :pr:`754`). +- Add RESTRICTED as constant to ChatMember (PR :pr:`761`). +- Add rich comparison to CallbackQuery (PR :pr:`764`). +- Fix get_game_high_scores (PR :pr:`771`). +- Warn on small con_pool_size during custom initalization of Updater (PR :pr:`793`). +- Catch exceptions in error handlerfor errors that happen during polling (PR :pr:`810`). +- For testing we switched to pytest (PR :pr:`788`). +- Lots of small improvements to our tests and documentation. + +.. _`see docs`: https://docs.python-telegram-bot.org/en/v13.11/telegram.ext.dispatcher.html?highlight=Dispatcher.add_handler#telegram.ext.Dispatcher.add_handler + +Version 7.0.1 +=============== +*Released 2017-07-28* + +- Fix TypeError exception in RegexHandler (PR #751). +- Small documentation fix (PR #749). + +Version 7.0.0 +============= +*Released 2017-07-25* + +- Fully support Bot API 3.2. +- New filters for handling messages from specific chat/user id (PR #677). +- Add the possibility to add objects as arguments to send_* methods (PR #742). +- Fixed download of URLs with UTF-8 chars in path (PR #688). +- Fixed URL parsing for ``Message`` text properties (PR #689). +- Fixed args dispatching in ``MessageQueue``'s decorator (PR #705). +- Fixed regression preventing IPv6 only hosts from connnecting to Telegram servers (Issue #720). +- ConvesationHandler - check if a user exist before using it (PR #699). +- Removed deprecated ``telegram.Emoji``. +- Removed deprecated ``Botan`` import from ``utils`` (``Botan`` is still available through ``contrib``). +- Removed deprecated ``ReplyKeyboardHide``. +- Removed deprecated ``edit_message`` argument of ``bot.set_game_score``. +- Internal restructure of files. +- Improved documentation. +- Improved unitests. + +Pre-version 7.0 +=============== + +**2017-06-18** + +*Released 6.1.0* + +- Fully support Bot API 3.0 +- Add more fine-grained filters for status updates +- Bug fixes and other improvements + +**2017-05-29** + +*Released 6.0.3* + +- Faulty PyPI release + +**2017-05-29** + +*Released 6.0.2* + +- Avoid confusion with user's ``urllib3`` by renaming vendored ``urllib3`` to ``ptb_urllib3`` + +**2017-05-19** + +*Released 6.0.1* + +- Add support for ``User.language_code`` +- Fix ``Message.text_html`` and ``Message.text_markdown`` for messages with emoji + +**2017-05-19** + +*Released 6.0.0* + +- Add support for Bot API 2.3.1 +- Add support for ``deleteMessage`` API method +- New, simpler API for ``JobQueue`` - :pr:`484` +- Download files into file-like objects - :pr:`459` +- Use vendor ``urllib3`` to address issues with timeouts + - The default timeout for messages is now 5 seconds. For sending media, the default timeout is now 20 seconds. +- String attributes that are not set are now ``None`` by default, instead of empty strings +- Add ``text_markdown`` and ``text_html`` properties to ``Message`` - :pr:`507` +- Add support for Socks5 proxy - :pr:`518` +- Add support for filters in ``CommandHandler`` - :pr:`536` +- Add the ability to invert (not) filters - :pr:`552` +- Add ``Filters.group`` and ``Filters.private`` +- Compatibility with GAE via ``urllib3.contrib`` package - :pr:`583` +- Add equality rich comparision operators to telegram objects - :pr:`604` +- Several bugfixes and other improvements +- Remove some deprecated code + +**2017-04-17** + +*Released 5.3.1* + +- Hotfix release due to bug introduced by urllib3 version 1.21 + +**2016-12-11** + +*Released 5.3* + +- Implement API changes of November 21st (Bot API 2.3) +- ``JobQueue`` now supports ``datetime.timedelta`` in addition to seconds +- ``JobQueue`` now supports running jobs only on certain days +- New ``Filters.reply`` filter +- Bugfix for ``Message.edit_reply_markup`` +- Other bugfixes + +**2016-10-25** + +*Released 5.2* + +- Implement API changes of October 3rd (games update) +- Add ``Message.edit_*`` methods +- Filters for the ``MessageHandler`` can now be combined using bitwise operators (``& and |``) +- Add a way to save user- and chat-related data temporarily +- Other bugfixes and improvements + +**2016-09-24** + +*Released 5.1* + +- Drop Python 2.6 support +- Deprecate ``telegram.Emoji`` + +- Use ``ujson`` if available +- Add instance methods to ``Message``, ``Chat``, ``User``, ``InlineQuery`` and ``CallbackQuery`` +- RegEx filtering for ``CallbackQueryHandler`` and ``InlineQueryHandler`` +- New ``MessageHandler`` filters: ``forwarded`` and ``entity`` +- Add ``Message.get_entity`` to correctly handle UTF-16 codepoints and ``MessageEntity`` offsets +- Fix bug in ``ConversationHandler`` when first handler ends the conversation +- Allow multiple ``Dispatcher`` instances +- Add ``ChatMigrated`` Exception +- Properly split and handle arguments in ``CommandHandler`` + +**2016-07-15** + +*Released 5.0* + +- Rework ``JobQueue`` +- Introduce ``ConversationHandler`` +- Introduce ``telegram.constants`` - :pr:`342` + +**2016-07-12** + +*Released 4.3.4* + +- Fix proxy support with ``urllib3`` when proxy requires auth + +**2016-07-08** + +*Released 4.3.3* + +- Fix proxy support with ``urllib3`` + +**2016-07-04** + +*Released 4.3.2* + +- Fix: Use ``timeout`` parameter in all API methods + +**2016-06-29** + +*Released 4.3.1* + +- Update wrong requirement: ``urllib3>=1.10`` + +**2016-06-28** + +*Released 4.3* + +- Use ``urllib3.PoolManager`` for connection re-use +- Rewrite ``run_async`` decorator to re-use threads +- New requirements: ``urllib3`` and ``certifi`` + +**2016-06-10** + +*Released 4.2.1* + +- Fix ``CallbackQuery.to_dict()`` bug (thanks to @jlmadurga) +- Fix ``editMessageText`` exception when receiving a ``CallbackQuery`` + +**2016-05-28** + +*Released 4.2* + +- Implement Bot API 2.1 +- Move ``botan`` module to ``telegram.contrib`` +- New exception type: ``BadRequest`` + +**2016-05-22** + +*Released 4.1.2* + +- Fix ``MessageEntity`` decoding with Bot API 2.1 changes + +**2016-05-16** + +*Released 4.1.1* + +- Fix deprecation warning in ``Dispatcher`` + +**2016-05-15** + +*Released 4.1* + +- Implement API changes from May 6, 2016 +- Fix bug when ``start_polling`` with ``clean=True`` +- Methods now have snake_case equivalent, for example ``telegram.Bot.send_message`` is the same as ``telegram.Bot.sendMessage`` + +**2016-05-01** + +*Released 4.0.3* + +- Add missing attribute ``location`` to ``InlineQuery`` + +**2016-04-29** + +*Released 4.0.2* + +- Bugfixes +- ``KeyboardReplyMarkup`` now accepts ``str`` again + +**2016-04-27** + +*Released 4.0.1* + +- Implement Bot API 2.0 +- Almost complete recode of ``Dispatcher`` +- Please read the `Transition Guide to 4.0 `_ +- **Changes from 4.0rc1** + - The syntax of filters for ``MessageHandler`` (upper/lower cases) + - Handler groups are now identified by ``int`` only, and ordered +- **Note:** v4.0 has been skipped due to a PyPI accident + +**2016-04-22** + +*Released 4.0rc1* + +- Implement Bot API 2.0 +- Almost complete recode of ``Dispatcher`` +- Please read the `Transistion Guide to 4.0 `_ + +**2016-03-22** + +*Released 3.4* + +- Move ``Updater``, ``Dispatcher`` and ``JobQueue`` to new ``telegram.ext`` submodule (thanks to @rahiel) +- Add ``disable_notification`` parameter (thanks to @aidarbiktimirov) +- Fix bug where commands sent by Telegram Web would not be recognized (thanks to @shelomentsevd) +- Add option to skip old updates on bot startup +- Send files from ``BufferedReader`` + +**2016-02-28** + +*Released 3.3* + +- Inline bots +- Send any file by URL +- Specialized exceptions: ``Unauthorized``, ``InvalidToken``, ``NetworkError`` and ``TimedOut`` +- Integration for botan.io (thanks to @ollmer) +- HTML Parsemode (thanks to @jlmadurga) +- Bugfixes and under-the-hood improvements + +**Very special thanks to Noam Meltzer (@tsnoam) for all of his work!** + +**2016-01-09** + +*Released 3.3b1* + +- Implement inline bots (beta) + +**2016-01-05** + +*Released 3.2.0* + +- Introducing ``JobQueue`` (original author: @franciscod) +- Streamlining all exceptions to ``TelegramError`` (Special thanks to @tsnoam) +- Proper locking of ``Updater`` and ``Dispatcher`` ``start`` and ``stop`` methods +- Small bugfixes + +**2015-12-29** + +*Released 3.1.2* + +- Fix custom path for file downloads +- Don't stop the dispatcher thread on uncaught errors in handlers + +**2015-12-21** + +*Released 3.1.1* + +- Fix a bug where asynchronous handlers could not have additional arguments +- Add ``groups`` and ``groupdict`` as additional arguments for regex-based handlers + +**2015-12-16** + +*Released 3.1.0* + +- The ``chat``-field in ``Message`` is now of type ``Chat``. (API update Oct 8 2015) +- ``Message`` now contains the optional fields ``supergroup_chat_created``, ``migrate_to_chat_id``, ``migrate_from_chat_id`` and ``channel_chat_created``. (API update Nov 2015) + +**2015-12-08** + +*Released 3.0.0* + +- Introducing the ``Updater`` and ``Dispatcher`` classes + +**2015-11-11** + +*Released 2.9.2* + +- Error handling on request timeouts has been improved + +**2015-11-10** + +*Released 2.9.1* + +- Add parameter ``network_delay`` to Bot.getUpdates for slow connections + +**2015-11-10** + +*Released 2.9* + +- Emoji class now uses ``bytes_to_native_str`` from ``future`` 3rd party lib +- Make ``user_from`` optional to work with channels +- Raise exception if Telegram times out on long-polling + +*Special thanks to @jh0ker for all hard work* + +**2015-10-08** + +*Released 2.8.7* + +- Type as optional for ``GroupChat`` class + +**2015-10-08** + +*Released 2.8.6* + +- Adds type to ``User`` and ``GroupChat`` classes (pre-release Telegram feature) + +**2015-09-24** + +*Released 2.8.5* + +- Handles HTTP Bad Gateway (503) errors on request +- Fixes regression on ``Audio`` and ``Document`` for unicode fields + +**2015-09-20** + +*Released 2.8.4* + +- ``getFile`` and ``File.download`` is now fully supported + +**2015-09-10** + +*Released 2.8.3* + +- Moved ``Bot._requestURL`` to its own class (``telegram.utils.request``) +- Much better, such wow, Telegram Objects tests +- Add consistency for ``str`` properties on Telegram Objects +- Better design to test if ``chat_id`` is invalid +- Add ability to set custom filename on ``Bot.sendDocument(..,filename='')`` +- Fix Sticker as ``InputFile`` +- Send JSON requests over urlencoded post data +- Markdown support for ``Bot.sendMessage(..., parse_mode=ParseMode.MARKDOWN)`` +- Refactor of ``TelegramError`` class (no more handling ``IOError`` or ``URLError``) + +**2015-09-05** + +*Released 2.8.2* + +- Fix regression on Telegram ReplyMarkup +- Add certificate to ``is_inputfile`` method + +**2015-09-05** + +*Released 2.8.1* + +- Fix regression on Telegram objects with thumb properties + +**2015-09-04** + +*Released 2.8* + +- TelegramError when ``chat_id`` is empty for send* methods +- ``setWebhook`` now supports sending self-signed certificate +- Huge redesign of existing Telegram classes +- Added support for PyPy +- Added docstring for existing classes + +**2015-08-19** + +*Released 2.7.1* + +- Fixed JSON serialization for ``message`` + +**2015-08-17** + +*Released 2.7* + +- Added support for ``Voice`` object and ``sendVoice`` method +- Due backward compatibility performer or/and title will be required for ``sendAudio`` +- Fixed JSON serialization when forwarded message + +**2015-08-15** + +*Released 2.6.1* + +- Fixed parsing image header issue on < Python 2.7.3 + +**2015-08-14** + +*Released 2.6.0* + +- Depreciation of ``require_authentication`` and ``clearCredentials`` methods +- Giving ``AUTHORS`` the proper credits for their contribution for this project +- ``Message.date`` and ``Message.forward_date`` are now ``datetime`` objects + +**2015-08-12** + +*Released 2.5.3* + +- ``telegram.Bot`` now supports to be unpickled + +**2015-08-11** + +*Released 2.5.2* + +- New changes from Telegram Bot API have been applied +- ``telegram.Bot`` now supports to be pickled +- Return empty ``str`` instead ``None`` when ``message.text`` is empty + +**2015-08-10** + +*Released 2.5.1* + +- Moved from GPLv2 to LGPLv3 + +**2015-08-09** + +*Released 2.5* + +- Fixes logging calls in API + +**2015-08-08** + +*Released 2.4* + +- Fixes ``Emoji`` class for Python 3 +- ``PEP8`` improvements + +**2015-08-08** + +*Released 2.3* + +- Fixes ``ForceReply`` class +- Remove ``logging.basicConfig`` from library + +**2015-07-25** + +*Released 2.2* + +- Allows ``debug=True`` when initializing ``telegram.Bot`` + +**2015-07-20** + +*Released 2.1* + +- Fix ``to_dict`` for ``Document`` and ``Video`` + +**2015-07-19** + +*Released 2.0* + +- Fixes bugs +- Improves ``__str__`` over ``to_json()`` +- Creates abstract class ``TelegramObject`` + +**2015-07-15** + +*Released 1.9* + +- Python 3 officially supported +- ``PEP8`` improvements + +**2015-07-12** + +*Released 1.8* + +- Fixes crash when replying an unicode text message (special thanks to JRoot3D) + +**2015-07-11** + +*Released 1.7* + +- Fixes crash when ``username`` is not defined on ``chat`` (special thanks to JRoot3D) + +**2015-07-10** + +*Released 1.6* + +- Improvements for GAE support + +**2015-07-10** + +*Released 1.5* + +- Fixes randomly unicode issues when using ``InputFile`` + +**2015-07-10** + +*Released 1.4* + +- ``requests`` lib is no longer required +- Google App Engine (GAE) is supported + +**2015-07-10** + +*Released 1.3* + +- Added support to ``setWebhook`` (special thanks to macrojames) + +**2015-07-09** + +*Released 1.2* + +- ``CustomKeyboard`` classes now available +- Emojis available +- ``PEP8`` improvements + +**2015-07-08** + +*Released 1.1* + +- PyPi package now available + +**2015-07-08** + +*Released 1.0* + +- Initial checkin of python-telegram-bot diff --git a/CODE_OF_CONDUCT.rst b/CODE_OF_CONDUCT.rst new file mode 100644 index 0000000000000000000000000000000000000000..5a1d6d26fad641e62d35922a44d3c81bdf302832 --- /dev/null +++ b/CODE_OF_CONDUCT.rst @@ -0,0 +1,52 @@ +==================================== +Contributor Covenant Code of Conduct +==================================== + +Our Pledge +========== + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +Our Standards +============= + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Publication of any content supporting, justifying or otherwise affiliating with terror and/or hate towards others +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +Our Responsibilities +==================== + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +Scope +===== + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +Enforcement +=========== + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at devs@python-telegram-bot.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +Attribution +=========== + +This Code of Conduct is adapted from the `Contributor Covenant `_, version 1.4, available at `https://www.contributor-covenant.org/version/1/4 `_. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..ae6b8d06be3c052fbf2f40d2a55fd8585d87ddd5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,619 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. [http://fsf.org/] + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. diff --git a/LICENSE.dual b/LICENSE.dual new file mode 100644 index 0000000000000000000000000000000000000000..c2730fdd406582213e828b4a7b1ed33c8b62ee24 --- /dev/null +++ b/LICENSE.dual @@ -0,0 +1,792 @@ + NOTICE: You can find here the GPLv3 license and after the Lesser GPLv3 license. +You may choose either license. + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. [http://fsf.org/] + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + + + + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. [http://fsf.org/] + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. diff --git a/LICENSE.lesser b/LICENSE.lesser new file mode 100644 index 0000000000000000000000000000000000000000..5cc63c20b453fb272056d6ce14398a593d303a90 --- /dev/null +++ b/LICENSE.lesser @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. [http://fsf.org/] + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md index b4d2a3b454f25dbb196ddf466a2b71707adefece..d0fe2ec6f3720d4caa032b98ab3b314d158f20da 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,15 @@ --- -title: Cardiffnlp Twitter Roberta Base Sentiment Latest -emoji: 💬 -colorFrom: yellow -colorTo: purple -sdk: gradio -sdk_version: 4.36.1 -app_file: app.py -pinned: false license: mit ---- - -An example chatbot using [Gradio](https://gradio.app), [`huggingface_hub`](https://huggingface.co/docs/huggingface_hub/v0.22.2/en/index), and the [Hugging Face Inference API](https://huggingface.co/docs/api-inference/index). \ No newline at end of file +datasets: +- HuggingFaceFV/finevideo +language: +- en +metrics: +- character +base_model: +- openai-community/gpt2 +pipeline_tag: text2text-generation +library_name: transformers +tags: +- code +--- \ No newline at end of file diff --git a/README.rst b/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..605942c06913709d676476340ce4c2ef6c5c9cf2 --- /dev/null +++ b/README.rst @@ -0,0 +1,235 @@ +.. image:: https://raw.githubusercontent.com/python-telegram-bot/logos/master/logo-text/png/ptb-logo-text_768.png + :align: center + :target: https://python-telegram-bot.org + :alt: python-telegram-bot Logo + +.. image:: https://img.shields.io/pypi/v/python-telegram-bot.svg + :target: https://pypi.org/project/python-telegram-bot/ + :alt: PyPi Package Version + +.. image:: https://img.shields.io/pypi/pyversions/python-telegram-bot.svg + :target: https://pypi.org/project/python-telegram-bot/ + :alt: Supported Python versions + +.. image:: https://img.shields.io/badge/Bot%20API-7.9-blue?logo=telegram + :target: https://core.telegram.org/bots/api-changelog + :alt: Supported Bot API version + +.. image:: https://img.shields.io/pypi/dm/python-telegram-bot + :target: https://pypistats.org/packages/python-telegram-bot + :alt: PyPi Package Monthly Download + +.. image:: https://readthedocs.org/projects/python-telegram-bot/badge/?version=stable + :target: https://docs.python-telegram-bot.org/en/stable/ + :alt: Documentation Status + +.. image:: https://img.shields.io/pypi/l/python-telegram-bot.svg + :target: https://www.gnu.org/licenses/lgpl-3.0.html + :alt: LGPLv3 License + +.. image:: https://github.com/python-telegram-bot/python-telegram-bot/actions/workflows/unit_tests.yml/badge.svg?branch=master + :target: https://github.com/python-telegram-bot/python-telegram-bot/ + :alt: Github Actions workflow + +.. image:: https://codecov.io/gh/python-telegram-bot/python-telegram-bot/branch/master/graph/badge.svg + :target: https://app.codecov.io/gh/python-telegram-bot/python-telegram-bot + :alt: Code coverage + +.. image:: https://isitmaintained.com/badge/resolution/python-telegram-bot/python-telegram-bot.svg + :target: https://isitmaintained.com/project/python-telegram-bot/python-telegram-bot + :alt: Median time to resolve an issue + +.. image:: https://api.codacy.com/project/badge/Grade/99d901eaa09b44b4819aec05c330c968 + :target: https://app.codacy.com/gh/python-telegram-bot/python-telegram-bot/dashboard + :alt: Code quality: Codacy + +.. image:: https://results.pre-commit.ci/badge/github/python-telegram-bot/python-telegram-bot/master.svg + :target: https://results.pre-commit.ci/latest/github/python-telegram-bot/python-telegram-bot/master + :alt: pre-commit.ci status + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + :alt: Code Style: Black + +.. image:: https://img.shields.io/badge/Telegram-Channel-blue.svg?logo=telegram + :target: https://t.me/pythontelegrambotchannel + :alt: Telegram Channel + +.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg?logo=telegram + :target: https://telegram.me/pythontelegrambotgroup + :alt: Telegram Group + +We have made you a wrapper you can't refuse + +We have a vibrant community of developers helping each other in our `Telegram group `_. Join us! + +*Stay tuned for library updates and new releases on our* `Telegram Channel `_. + +Introduction +------------ + +This library provides a pure Python, asynchronous interface for the +`Telegram Bot API `_. +It's compatible with Python versions **3.8+**. + +In addition to the pure API implementation, this library features several convenience methods and shortcuts as well as a number of high-level classes to +make the development of bots easy and straightforward. These classes are contained in the +``telegram.ext`` submodule. + +After installing_ the library, be sure to check out the section on `working with PTB`_. + +Telegram API support +~~~~~~~~~~~~~~~~~~~~ + +All types and methods of the Telegram Bot API **7.9** are natively supported by this library. +In addition, Bot API functionality not yet natively included can still be used as described `in our wiki `_. + +Notable Features +~~~~~~~~~~~~~~~~ + +- `Fully asynchronous `_ +- Convenient shortcut methods, e.g. `Message.reply_text `_ +- `Fully annotated with static type hints `_ +- `Customizable and extendable interface `_ +- Seamless integration with `webhooks `_ and `polling `_ +- `Comprehensive documentation and examples <#working-with-ptb>`_ + +Installing +---------- + +You can install or upgrade ``python-telegram-bot`` via + +.. code:: shell + + $ pip install python-telegram-bot --upgrade + +To install a pre-release, use the ``--pre`` `flag `_ in addition. + +You can also install ``python-telegram-bot`` from source, though this is usually not necessary. + +.. code:: shell + + $ git clone https://github.com/python-telegram-bot/python-telegram-bot + $ cd python-telegram-bot + $ pip install build + $ python -m build + +Verifying Releases +~~~~~~~~~~~~~~~~~~ + +To enable you to verify that a release file that you downloaded was indeed provided by the ``python-telegram-bot`` team, we have taken the following measures. + +Starting with v21.4, all releases are signed via `sigstore `_. +The corresponding signature files are uploaded to the `GitHub releases page`_. +To verify the signature, please install the `sigstore Python client `_ and follow the instructions for `verifying signatures from GitHub Actions `_. As input for the ``--repository`` parameter, please use the value ``python-telegram-bot/python-telegram-bot``. + +Earlier releases are signed with a GPG key. +The signatures are uploaded to both the `GitHub releases page`_ and the `PyPI project `_ and end with a suffix ``.asc``. +Please find the public keys `here `_. +The keys are named in the format ``-.gpg``. + +In addition, the GitHub release page also contains the sha1 hashes of the release files in the files with the suffix ``.sha1``. + +Dependencies & Their Versions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``python-telegram-bot`` tries to use as few 3rd party dependencies as possible. +However, for some features using a 3rd party library is more sane than implementing the functionality again. +As these features are *optional*, the corresponding 3rd party dependencies are not installed by default. +Instead, they are listed as optional dependencies. +This allows to avoid unnecessary dependency conflicts for users who don't need the optional features. + +The only required dependency is `httpx ~= 0.27 `_ for +``telegram.request.HTTPXRequest``, the default networking backend. + +``python-telegram-bot`` is most useful when used along with additional libraries. +To minimize dependency conflicts, we try to be liberal in terms of version requirements on the (optional) dependencies. +On the other hand, we have to ensure stability of ``python-telegram-bot``, which is why we do apply version bounds. +If you encounter dependency conflicts due to these bounds, feel free to reach out. + +Optional Dependencies +##################### + +PTB can be installed with optional dependencies: + +* ``pip install "python-telegram-bot[passport]"`` installs the `cryptography>=39.0.1 `_ library. Use this, if you want to use Telegram Passport related functionality. +* ``pip install "python-telegram-bot[socks]"`` installs `httpx[socks] `_. Use this, if you want to work behind a Socks5 server. +* ``pip install "python-telegram-bot[http2]"`` installs `httpx[http2] `_. Use this, if you want to use HTTP/2. +* ``pip install "python-telegram-bot[rate-limiter]"`` installs `aiolimiter~=1.1.0 `_. Use this, if you want to use ``telegram.ext.AIORateLimiter``. +* ``pip install "python-telegram-bot[webhooks]"`` installs the `tornado~=6.4 `_ library. Use this, if you want to use ``telegram.ext.Updater.start_webhook``/``telegram.ext.Application.run_webhook``. +* ``pip install "python-telegram-bot[callback-data]"`` installs the `cachetools>=5.3.3,<5.6.0 `_ library. Use this, if you want to use `arbitrary callback_data `_. +* ``pip install "python-telegram-bot[job-queue]"`` installs the `APScheduler~=3.10.4 `_ library and enforces `pytz>=2018.6 `_, where ``pytz`` is a dependency of ``APScheduler``. Use this, if you want to use the ``telegram.ext.JobQueue``. + +To install multiple optional dependencies, separate them by commas, e.g. ``pip install "python-telegram-bot[socks,webhooks]"``. + +Additionally, two shortcuts are provided: + +* ``pip install "python-telegram-bot[all]"`` installs all optional dependencies. +* ``pip install "python-telegram-bot[ext]"`` installs all optional dependencies that are related to ``telegram.ext``, i.e. ``[rate-limiter, webhooks, callback-data, job-queue]``. + +Working with PTB +---------------- + +Once you have installed the library, you can begin working with it - so let's get started! + +Quick Start +~~~~~~~~~~~ + +Our Wiki contains an `Introduction to the API `_ explaining how the pure Bot API can be accessed via ``python-telegram-bot``. +Moreover, the `Tutorial: Your first Bot `_ gives an introduction on how chatbots can be easily programmed with the help of the ``telegram.ext`` module. + +Resources +~~~~~~~~~ + +- The `package documentation `_ is the technical reference for ``python-telegram-bot``. + It contains descriptions of all available classes, modules, methods and arguments as well as the `changelog `_. +- The `wiki `_ is home to number of more elaborate introductions of the different features of ``python-telegram-bot`` and other useful resources that go beyond the technical documentation. +- Our `examples section `_ contains several examples that showcase the different features of both the Bot API and ``python-telegram-bot``. + Even if it is not your approach for learning, please take a look at ``echobot.py``. It is the de facto base for most of the bots out there. + The code for these examples is released to the public domain, so you can start by grabbing the code and building on top of it. +- The `official Telegram Bot API documentation `_ is of course always worth a read. + +Getting help +~~~~~~~~~~~~ + +If the resources mentioned above don't answer your questions or simply overwhelm you, there are several ways of getting help. + +1. We have a vibrant community of developers helping each other in our `Telegram group `_. Join us! Asking a question here is often the quickest way to get a pointer in the right direction. + +2. Ask questions by opening `a discussion `_. + +3. You can even ask for help on Stack Overflow using the `python-telegram-bot tag `_. + +Concurrency +~~~~~~~~~~~ + +Since v20.0, ``python-telegram-bot`` is built on top of Pythons ``asyncio`` module. +Because ``asyncio`` is in general single-threaded, ``python-telegram-bot`` does currently not aim to be thread-safe. +Noteworthy parts of ``python-telegram-bots`` API that are likely to cause issues (e.g. race conditions) when used in a multi-threaded setting include: + +* ``telegram.ext.Application/Updater.update_queue`` +* ``telegram.ext.ConversationHandler.check/handle_update`` +* ``telegram.ext.CallbackDataCache`` +* ``telegram.ext.BasePersistence`` +* all classes in the ``telegram.ext.filters`` module that allow to add/remove allowed users/chats at runtime + +Contributing +------------ + +Contributions of all sizes are welcome. +Please review our `contribution guidelines `_ to get started. +You can also help by `reporting bugs or feature requests `_. + +Donating +-------- +Occasionally we are asked if we accept donations to support the development. +While we appreciate the thought, maintaining PTB is our hobby, and we have almost no running costs for it. We therefore have nothing set up to accept donations. +If you still want to donate, we kindly ask you to donate to another open source project/initiative of your choice instead. + +License +------- + +You may copy, distribute and modify the software provided that modifications are described and licensed for free under `LGPL-3 `_. +Derivatives works (including modifications or anything statically linked to the library) can only be redistributed under LGPL-3, but applications that use the library don't have to be. + +.. _`GitHub releases page`: https://github.com/python-telegram-bot/python-telegram-bot/releases> diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/__main__.py b/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..6a508e3574b14e7809ed887e7e884c54da35a6bf --- /dev/null +++ b/__main__.py @@ -0,0 +1,54 @@ +# !/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=missing-module-docstring +# ruff: noqa: T201, D100, S603, S607 +import subprocess +import sys +from typing import Optional + +from . import __version__ as telegram_ver +from .constants import BOT_API_VERSION + + +def _git_revision() -> Optional[str]: + try: + output = subprocess.check_output( + ["git", "describe", "--long", "--tags"], stderr=subprocess.STDOUT + ) + except (subprocess.SubprocessError, OSError): + return None + return output.decode().strip() + + +def print_ver_info() -> None: + """Prints version information for python-telegram-bot, the Bot API and Python.""" + git_revision = _git_revision() + print(f"python-telegram-bot {telegram_ver}" + (f" ({git_revision})" if git_revision else "")) + print(f"Bot API {BOT_API_VERSION}") + sys_version = sys.version.replace("\n", " ") + print(f"Python {sys_version}") + + +def main() -> None: + """Prints version information for python-telegram-bot, the Bot API and Python.""" + print_ver_info() + + +if __name__ == "__main__": + main() diff --git a/_birthdate.py b/_birthdate.py new file mode 100644 index 0000000000000000000000000000000000000000..06caf67d5ec96a0163c2d8c086fc0f9875366d4c --- /dev/null +++ b/_birthdate.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram Birthday.""" +from datetime import date +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class Birthdate(TelegramObject): + """ + This object describes the birthdate of a user. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`day`, and :attr:`month` are equal. + + .. versionadded:: 21.1 + + Args: + day (:obj:`int`): Day of the user's birth; 1-31. + month (:obj:`int`): Month of the user's birth; 1-12. + year (:obj:`int`, optional): Year of the user's birth. + + Attributes: + day (:obj:`int`): Day of the user's birth; 1-31. + month (:obj:`int`): Month of the user's birth; 1-12. + year (:obj:`int`): Optional. Year of the user's birth. + + """ + + __slots__ = ("day", "month", "year") + + def __init__( + self, + day: int, + month: int, + year: Optional[int] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + # Required + self.day: int = day + self.month: int = month + # Optional + self.year: Optional[int] = year + + self._id_attrs = ( + self.day, + self.month, + ) + + self._freeze() + + def to_date(self, year: Optional[int] = None) -> date: + """Return the birthdate as a date object. + + .. versionchanged:: 21.2 + Now returns a :obj:`datetime.date` object instead of a :obj:`datetime.datetime` object, + as was originally intended. + + Args: + year (:obj:`int`, optional): The year to use. Required, if the :attr:`year` was not + present. + + Returns: + :obj:`datetime.date`: The birthdate as a date object. + """ + if self.year is None and year is None: + raise ValueError( + "The `year` argument is required if the `year` attribute was not present." + ) + + return date(year or self.year, self.month, self.day) # type: ignore[arg-type] diff --git a/_bot.py b/_bot.py new file mode 100644 index 0000000000000000000000000000000000000000..b79df08ff1738e63f4e92e2f1fbdc64570490fc9 --- /dev/null +++ b/_bot.py @@ -0,0 +1,9649 @@ +#!/usr/bin/env python +# pylint: disable=too-many-arguments +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram Bot.""" + +import asyncio +import contextlib +import copy +import pickle +from datetime import datetime +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + AsyncContextManager, + Callable, + Dict, + List, + NoReturn, + Optional, + Sequence, + Set, + Tuple, + Type, + TypeVar, + Union, + cast, + no_type_check, +) + +try: + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import serialization + + CRYPTO_INSTALLED = True +except ImportError: + default_backend = None # type: ignore[assignment] + serialization = None # type: ignore[assignment] + CRYPTO_INSTALLED = False + +from telegram._botcommand import BotCommand +from telegram._botcommandscope import BotCommandScope +from telegram._botdescription import BotDescription, BotShortDescription +from telegram._botname import BotName +from telegram._business import BusinessConnection +from telegram._chatadministratorrights import ChatAdministratorRights +from telegram._chatboost import UserChatBoosts +from telegram._chatfullinfo import ChatFullInfo +from telegram._chatinvitelink import ChatInviteLink +from telegram._chatmember import ChatMember +from telegram._chatpermissions import ChatPermissions +from telegram._files.animation import Animation +from telegram._files.audio import Audio +from telegram._files.chatphoto import ChatPhoto +from telegram._files.contact import Contact +from telegram._files.document import Document +from telegram._files.file import File +from telegram._files.inputmedia import InputMedia, InputPaidMedia +from telegram._files.location import Location +from telegram._files.photosize import PhotoSize +from telegram._files.sticker import MaskPosition, Sticker, StickerSet +from telegram._files.venue import Venue +from telegram._files.video import Video +from telegram._files.videonote import VideoNote +from telegram._files.voice import Voice +from telegram._forumtopic import ForumTopic +from telegram._games.gamehighscore import GameHighScore +from telegram._inline.inlinequeryresultsbutton import InlineQueryResultsButton +from telegram._menubutton import MenuButton +from telegram._message import Message +from telegram._messageid import MessageId +from telegram._payment.stars import StarTransactions +from telegram._poll import InputPollOption, Poll +from telegram._reaction import ReactionType, ReactionTypeCustomEmoji, ReactionTypeEmoji +from telegram._reply import ReplyParameters +from telegram._sentwebappmessage import SentWebAppMessage +from telegram._telegramobject import TelegramObject +from telegram._update import Update +from telegram._user import User +from telegram._userprofilephotos import UserProfilePhotos +from telegram._utils.argumentparsing import parse_lpo_and_dwpp, parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue +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.warnings import warn +from telegram._webhookinfo import WebhookInfo +from telegram.constants import InlineQueryLimit, ReactionEmoji +from telegram.error import EndPointNotFound, InvalidToken +from telegram.request import BaseRequest, RequestData +from telegram.request._httpxrequest import HTTPXRequest +from telegram.request._requestparameter import RequestParameter +from telegram.warnings import PTBDeprecationWarning, PTBUserWarning + +if TYPE_CHECKING: + from telegram import ( + InlineKeyboardMarkup, + InlineQueryResult, + InputFile, + InputMediaAudio, + InputMediaDocument, + InputMediaPhoto, + InputMediaVideo, + InputSticker, + LabeledPrice, + LinkPreviewOptions, + MessageEntity, + PassportElementError, + ShippingOption, + ) + +BT = TypeVar("BT", bound="Bot") + + +class Bot(TelegramObject, AsyncContextManager["Bot"]): + """This object represents a Telegram Bot. + + Instances of this class can be used as asyncio context managers, where + + .. code:: python + + async with bot: + # code + + is roughly equivalent to + + .. code:: python + + try: + await bot.initialize() + # code + finally: + await bot.shutdown() + + .. seealso:: :meth:`__aenter__` and :meth:`__aexit__`. + + Note: + * Most bot methods have the argument ``api_kwargs`` which allows passing arbitrary keywords + to the Telegram API. This can be used to access new features of the API before they are + incorporated into PTB. The limitations to this argument are the same as the ones + described in :meth:`do_api_request`. + * Bots should not be serialized since if you for e.g. change the bots token, then your + serialized instance will not reflect that change. Trying to pickle a bot instance will + raise :exc:`pickle.PicklingError`. Trying to deepcopy a bot instance will raise + :exc:`TypeError`. + + Examples: + :any:`Raw API Bot ` + + .. seealso:: :wiki:`Your First Bot `, + :wiki:`Builder Pattern ` + + .. versionadded:: 13.2 + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`bot` is equal. + + .. versionchanged:: 20.0 + + * Removed the deprecated methods ``kick_chat_member``, ``kickChatMember``, + ``get_chat_members_count`` and ``getChatMembersCount``. + * Removed the deprecated property ``commands``. + * Removed the deprecated ``defaults`` parameter. If you want to use + :class:`telegram.ext.Defaults`, please use the subclass :class:`telegram.ext.ExtBot` + instead. + * Attempting to pickle a bot instance will now raise :exc:`pickle.PicklingError`. + * Attempting to deepcopy a bot instance will now raise :exc:`TypeError`. + * The following are now keyword-only arguments in Bot methods: + ``location``, ``filename``, ``venue``, ``contact``, + ``{read, write, connect, pool}_timeout``, ``api_kwargs``. Use a named argument for those, + and notice that some positional arguments changed position as a result. + * For uploading files, file paths are now always accepted. If :paramref:`local_mode` is + :obj:`False`, the file contents will be read in binary mode and uploaded. Otherwise, + the file path will be passed in the + `file URI scheme `_. + + .. versionchanged:: 20.5 + Removed deprecated methods ``set_sticker_set_thumb`` and ``setStickerSetThumb``. + Use :meth:`set_sticker_set_thumbnail` and :meth:`setStickerSetThumbnail` instead. + + Args: + token (:obj:`str`): Bot's unique authentication token. + base_url (:obj:`str`, optional): Telegram Bot API service URL. + base_file_url (:obj:`str`, optional): Telegram Bot API file URL. + 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 + :class:`telegram.request.HTTPXRequest` will be used. + get_updates_request (:class:`telegram.request.BaseRequest`, optional): Pre initialized + :class:`telegram.request.BaseRequest` instances. Will be used exclusively for + :meth:`get_updates`. If not passed, an instance of + :class:`telegram.request.HTTPXRequest` will be used. + private_key (:obj:`bytes`, optional): Private key for decryption of telegram passport data. + private_key_password (:obj:`bytes`, optional): Password for above private key. + local_mode (:obj:`bool`, optional): Set to :obj:`True`, if the :paramref:`base_url` is + the URI of a `Local Bot API Server `_ that runs with the ``--local`` flag. Currently, the only effect of + this is that files are uploaded using their local path in the + `file URI scheme `_. + Defaults to :obj:`False`. + + .. versionadded:: 20.0. + + .. include:: inclusions/bot_methods.rst + + .. |removed_thumb_arg| replace:: Removed deprecated argument ``thumb``. Use + ``thumbnail`` instead. + + """ + + # This is a class variable since we want to override the logger name in ExtBot + # without having to change all places where this is used + _LOGGER = get_logger(__name__) + + __slots__ = ( + "_base_file_url", + "_base_url", + "_bot_user", + "_initialized", + "_local_mode", + "_private_key", + "_request", + "_token", + ) + + def __init__( + self, + token: str, + base_url: str = "https://api.telegram.org/bot", + base_file_url: str = "https://api.telegram.org/file/bot", + request: Optional[BaseRequest] = None, + get_updates_request: Optional[BaseRequest] = None, + private_key: Optional[bytes] = None, + private_key_password: Optional[bytes] = None, + local_mode: bool = False, + ): + super().__init__(api_kwargs=None) + if not token: + 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._local_mode: bool = local_mode + self._bot_user: Optional[User] = None + self._private_key: Optional[bytes] = None + self._initialized: bool = False + + self._request: Tuple[BaseRequest, BaseRequest] = ( + HTTPXRequest() if get_updates_request is None else get_updates_request, + HTTPXRequest() if request is None else request, + ) + + # 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. + + warning_string = "" + + if ( + isinstance(self._request[0], HTTPXRequest) + and self._request[0].http_version == "2" + and not 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") + ): + if warning_string: + warning_string += " and request" + else: + warning_string = "request" + + if warning_string: + self._warn( + f"You set the HTTP version for the {warning_string} HTTPXRequest instance to " + "HTTP/2. The self hosted bot api instances only support HTTP/1.1. You should " + "either run a HTTP proxy in front of it which supports HTTP/2 or use HTTP/1.1.", + PTBUserWarning, + stacklevel=2, + ) + + if private_key: + if not CRYPTO_INSTALLED: + raise RuntimeError( + "To use Telegram Passports, PTB must be installed via `pip install " + '"python-telegram-bot[passport]"`.' + ) + self._private_key = serialization.load_pem_private_key( + private_key, password=private_key_password, backend=default_backend() + ) + + self._freeze() + + async def __aenter__(self: BT) -> BT: + """ + |async_context_manager| :meth:`initializes ` the Bot. + + Returns: + The initialized Bot instance. + + Raises: + :exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown` + is called in this case. + """ + try: + await self.initialize() + except Exception: + await self.shutdown() + raise + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + """|async_context_manager| :meth:`shuts down ` the Bot.""" + # Make sure not to return `True` so that exceptions are not suppressed + # https://docs.python.org/3/reference/datamodel.html?#object.__aexit__ + await self.shutdown() + + def __reduce__(self) -> NoReturn: + """Customizes how :func:`copy.deepcopy` processes objects of this type. Bots can not + be pickled and this method will always raise an exception. + + .. versionadded:: 20.0 + + Raises: + :exc:`pickle.PicklingError` + """ + raise pickle.PicklingError("Bot objects cannot be pickled!") + + def __deepcopy__(self, memodict: Dict[int, object]) -> NoReturn: + """Customizes how :func:`copy.deepcopy` processes objects of this type. Bots can not + be deepcopied and this method will always raise an exception. + + .. versionadded:: 20.0 + + Raises: + :exc:`TypeError` + """ + raise TypeError("Bot objects cannot be deepcopied!") + + def __eq__(self, other: object) -> bool: + """Defines equality condition for the :class:`telegram.Bot` object. + Two objects of this class are considered to be equal if their attributes + :attr:`bot` are equal. + + Returns: + :obj:`True` if both attributes :attr:`bot` are equal. :obj:`False` otherwise. + """ + if isinstance(other, Bot): + return self.bot == other.bot + return super().__eq__(other) + + def __hash__(self) -> int: + """See :meth:`telegram.TelegramObject.__hash__`""" + if self._bot_user is None: + return super().__hash__() + return hash((self.bot, Bot)) + + def __repr__(self) -> str: + """Give a string representation of the bot in the form ``Bot[token=...]``. + + As this class doesn't implement :meth:`object.__str__`, the default implementation + will be used, which is equivalent to :meth:`__repr__`. + + Returns: + :obj:`str` + """ + return build_repr_with_selected_attrs(self, token=self.token) + + @property + def token(self) -> str: + """:obj:`str`: Bot's unique authentication token. + + .. versionadded:: 20.0 + """ + return self._token + + @property + def base_url(self) -> str: + """:obj:`str`: Telegram Bot API service URL, built from :paramref:`Bot.base_url` and + :paramref:`Bot.token`. + + .. versionadded:: 20.0 + """ + return self._base_url + + @property + def base_file_url(self) -> str: + """:obj:`str`: Telegram Bot API file URL, built from :paramref:`Bot.base_file_url` and + :paramref:`Bot.token`. + + .. versionadded:: 20.0 + """ + return self._base_file_url + + @property + def local_mode(self) -> bool: + """:obj:`bool`: Whether this bot is running in local mode. + + .. versionadded:: 20.0 + """ + return self._local_mode + + # Proper type hints are difficult because: + # 1. cryptography doesn't have a nice base class, so it would get lengthy + # 2. we can't import cryptography if it's not installed + @property + def private_key(self) -> Optional[Any]: + """Deserialized private key for decryption of telegram passport data. + + .. versionadded:: 20.0 + """ + return self._private_key + + @property + def request(self) -> BaseRequest: + """The :class:`~telegram.request.BaseRequest` object used by this bot. + + Warning: + Requests to the Bot API are made by the various methods of this class. This attribute + should *not* be used manually. + """ + return self._request[1] + + @property + def bot(self) -> User: + """:class:`telegram.User`: User instance for the bot as returned by :meth:`get_me`. + + Warning: + This value is the cached return value of :meth:`get_me`. If the bots profile is + changed during runtime, this value won't reflect the changes until :meth:`get_me` is + called again. + + .. seealso:: :meth:`initialize` + """ + if self._bot_user is None: + raise RuntimeError( + f"{self.__class__.__name__} is not properly initialized. Call " + f"`{self.__class__.__name__}.initialize` before accessing this property." + ) + return self._bot_user + + @property + def id(self) -> int: + """:obj:`int`: Unique identifier for this bot. Shortcut for the corresponding attribute of + :attr:`bot`. + """ + return self.bot.id + + @property + def first_name(self) -> str: + """:obj:`str`: Bot's first name. Shortcut for the corresponding attribute of + :attr:`bot`. + """ + return self.bot.first_name + + @property + def last_name(self) -> str: + """:obj:`str`: Optional. Bot's last name. Shortcut for the corresponding attribute of + :attr:`bot`. + """ + return self.bot.last_name # type: ignore + + @property + def username(self) -> str: + """:obj:`str`: Bot's username. Shortcut for the corresponding attribute of + :attr:`bot`. + """ + return self.bot.username # type: ignore + + @property + def link(self) -> str: + """:obj:`str`: Convenience property. Returns the t.me link of the bot.""" + return f"https://t.me/{self.username}" + + @property + def can_join_groups(self) -> bool: + """:obj:`bool`: Bot's :attr:`telegram.User.can_join_groups` attribute. Shortcut for the + corresponding attribute of :attr:`bot`. + """ + return self.bot.can_join_groups # type: ignore + + @property + def can_read_all_group_messages(self) -> bool: + """:obj:`bool`: Bot's :attr:`telegram.User.can_read_all_group_messages` attribute. + Shortcut for the corresponding attribute of :attr:`bot`. + """ + return self.bot.can_read_all_group_messages # type: ignore + + @property + def supports_inline_queries(self) -> bool: + """:obj:`bool`: Bot's :attr:`telegram.User.supports_inline_queries` attribute. + Shortcut for the corresponding attribute of :attr:`bot`. + """ + return self.bot.supports_inline_queries # type: ignore + + @property + def name(self) -> str: + """:obj:`str`: Bot's @username. Shortcut for the corresponding attribute of :attr:`bot`.""" + return f"@{self.username}" + + @classmethod + def _warn( + cls, + message: Union[str, PTBUserWarning], + category: Type[Warning] = PTBUserWarning, + stacklevel: int = 0, + ) -> None: + """Convenience method to issue a warning. This method is here mostly to make it easier + for ExtBot to add 1 level to all warning calls. + """ + warn(message=message, category=category, stacklevel=stacklevel + 1) + + def _parse_file_input( + self, + file_input: Union[FileInput, "TelegramObject"], + tg_type: Optional[Type["TelegramObject"]] = None, + filename: Optional[str] = None, + attach: bool = False, + ) -> Union[str, "InputFile", Any]: + return parse_file_input( + file_input=file_input, + tg_type=tg_type, + filename=filename, + attach=attach, + local_mode=self._local_mode, + ) + + def _insert_defaults(self, data: Dict[str, object]) -> None: + """This method is here to make ext.Defaults work. Because we need to be able to tell + e.g. `send_message(chat_id, text)` from `send_message(chat_id, text, parse_mode=None)`, the + default values for `parse_mode` etc are not `None` but `DEFAULT_NONE`. While this *could* + be done in ExtBot instead of Bot, shortcuts like `Message.reply_text` need to work for both + Bot and ExtBot, so they also have the `DEFAULT_NONE` default values. + + This makes it necessary to convert `DefaultValue(obj)` to `obj` at some point between + `Message.reply_text` and the request to TG. Doing this here in a centralized manner is a + rather clean and minimally invasive solution, i.e. the link between tg and tg.ext is as + small as possible. + See also _insert_defaults_for_ilq + ExtBot overrides this method to actually insert default values. + + If in the future we come up with a better way of making `Defaults` work, we can cut this + link as well. + """ + # We + # 1) set the correct parse_mode for all InputMedia objects + # 2) replace all DefaultValue instances with the corresponding normal value. + for key, val in data.items(): + # 1) + if isinstance(val, InputMedia): + # Copy object as not to edit it in-place + new = copy.copy(val) + with new._unfrozen(): + new.parse_mode = DefaultValue.get_value(new.parse_mode) + data[key] = new + elif ( + key == "media" + and isinstance(val, Sequence) + and not isinstance(val[0], InputPaidMedia) + ): + # Copy objects as not to edit them in-place + copy_list = [copy.copy(media) for media in val] + for media in copy_list: + with media._unfrozen(): + media.parse_mode = DefaultValue.get_value(media.parse_mode) + data[key] = copy_list + # 2) + else: + data[key] = DefaultValue.get_value(val) + + async def _post( + self, + endpoint: str, + data: Optional[JSONDict] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Any: + # We know that the return type is Union[bool, JSONDict, List[JSONDict]], but it's hard + # to tell mypy which methods expects which of these return values and `Any` saves us a + # lot of `type: ignore` comments + if data is None: + data = {} + + if api_kwargs: + data.update(api_kwargs) + + # Insert is in-place, so no return value for data + self._insert_defaults(data) + + # Drop any None values because Telegram doesn't handle them well + data = {key: value for key, value in data.items() if value is not None} + + return await self._do_post( + endpoint=endpoint, + data=data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + ) + + async def _do_post( + self, + endpoint: str, + data: JSONDict, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + ) -> Union[bool, JSONDict, List[JSONDict]]: + # This also converts datetimes into timestamps. + # We don't do this earlier so that _insert_defaults (see above) has a chance to convert + # to the default timezone in case this is called by ExtBot + request_data = RequestData( + parameters=[RequestParameter.from_input(key, value) for key, value in data.items()], + ) + + request = self._request[0] if endpoint == "getUpdates" else self._request[1] + + self._LOGGER.debug("Calling Bot API endpoint `%s` with parameters `%s`", endpoint, data) + result = await request.post( + url=f"{self._base_url}/{endpoint}", + request_data=request_data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + ) + self._LOGGER.debug( + "Call to Bot API endpoint `%s` finished with return value `%s`", endpoint, result + ) + + return result + + async def _send_message( + self, + endpoint: str, + data: JSONDict, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + link_preview_options: ODVInput["LinkPreviewOptions"] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Any: + """Protected method to send or edit messages of any type. + + It is here to reduce repetition of if-else closes in the different bot methods, + i.e. this method takes care of adding its parameters to `data` if appropriate. + + Depending on the bot method, returns either `True` or the message. + However, it's hard to tell mypy which methods expects which of these return values and + using `Any` instead saves us a lot of `type: ignore` comments + """ + # We don't check if (DEFAULT_)None here, so that _post is able to insert the defaults + # correctly, if necessary: + if allow_sending_without_reply is not DEFAULT_NONE and reply_parameters is not None: + raise ValueError( + "`allow_sending_without_reply` and `reply_parameters` are mutually exclusive." + ) + + if reply_to_message_id is not None and reply_parameters is not None: + raise ValueError( + "`reply_to_message_id` and `reply_parameters` are mutually exclusive." + ) + + if reply_to_message_id is not None: + reply_parameters = ReplyParameters( + message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, + ) + + data["disable_notification"] = disable_notification + data["protect_content"] = protect_content + data["parse_mode"] = parse_mode + + if reply_parameters is not None: + data["reply_parameters"] = reply_parameters + + if link_preview_options is not None: + data["link_preview_options"] = link_preview_options + + if reply_markup is not None: + data["reply_markup"] = reply_markup + + if message_thread_id is not None: + data["message_thread_id"] = message_thread_id + + if caption is not None: + data["caption"] = caption + + if caption_entities is not None: + data["caption_entities"] = caption_entities + + if business_connection_id is not None: + data["business_connection_id"] = business_connection_id + + if message_effect_id is not None: + data["message_effect_id"] = message_effect_id + + result = await self._post( + endpoint, + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + if result is True: + return result + + return Message.de_json(result, self) + + async def initialize(self) -> None: + """Initialize resources used by this class. Currently calls :meth:`get_me` to + cache :attr:`bot` and calls :meth:`telegram.request.BaseRequest.initialize` for + the request objects used by this bot. + + .. seealso:: :meth:`shutdown` + + .. versionadded:: 20.0 + """ + if self._initialized: + self._LOGGER.debug("This Bot is already initialized.") + return + + await asyncio.gather(self._request[0].initialize(), self._request[1].initialize()) + # Since the bot is to be initialized only once, we can also use it for + # verifying the token passed and raising an exception if it's invalid. + try: + await self.get_me() + except InvalidToken as exc: + raise InvalidToken(f"The token `{self._token}` was rejected by the server.") from exc + self._initialized = True + + async def shutdown(self) -> None: + """Stop & clear resources used by this class. Currently just calls + :meth:`telegram.request.BaseRequest.shutdown` for the request objects used by this bot. + + .. seealso:: :meth:`initialize` + + .. versionadded:: 20.0 + """ + if not self._initialized: + self._LOGGER.debug("This Bot is already shut down. Returning.") + return + + await asyncio.gather(self._request[0].shutdown(), self._request[1].shutdown()) + self._initialized = False + + async def do_api_request( + self, + endpoint: str, + api_kwargs: Optional[JSONDict] = None, + return_type: Optional[Type[TelegramObject]] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + ) -> Any: + """Do a request to the Telegram API. + + This method is here to make it easier to use new API methods that are not yet supported + by this library. + + Hint: + Since PTB does not know which arguments are passed to this method, some caution is + necessary in terms of PTBs utility functionalities. In particular + + * passing objects of any class defined in the :mod:`telegram` module is supported + * when uploading files, a :class:`telegram.InputFile` must be passed as the value for + the corresponding argument. Passing a file path or file-like object will not work. + File paths will work only in combination with :paramref:`~Bot.local_mode`. + * when uploading files, PTB can still correctly determine that + a special write timeout value should be used instead of the default + :paramref:`telegram.request.HTTPXRequest.write_timeout`. + * insertion of default values specified via :class:`telegram.ext.Defaults` will not + work (only relevant for :class:`telegram.ext.ExtBot`). + * The only exception is :class:`telegram.ext.Defaults.tzinfo`, which will be correctly + applied to :class:`datetime.datetime` objects. + + .. versionadded:: 20.8 + + Args: + endpoint (:obj:`str`): The API endpoint to use, e.g. ``getMe`` or ``get_me``. + api_kwargs (:obj:`dict`, optional): The keyword arguments to pass to the API call. + If not specified, no arguments are passed. + return_type (:class:`telegram.TelegramObject`, optional): If specified, the result of + the API call will be deserialized into an instance of this class or tuple of + instances of this class. If not specified, the raw result of the API call will be + returned. + + Returns: + The result of the API call. If :paramref:`return_type` is not specified, this is a + :obj:`dict` or :obj:`bool`, otherwise an instance of :paramref:`return_type` or a + tuple of :paramref:`return_type`. + + Raises: + :class:`telegram.error.TelegramError` + """ + if hasattr(self, endpoint): + self._warn( + ( + f"Please use 'Bot.{endpoint}' instead of " + f"'Bot.do_api_request(\"{endpoint}\", ...)'" + ), + stacklevel=2, + ) + + camel_case_endpoint = to_camel_case(endpoint) + try: + result = await self._post( + camel_case_endpoint, + api_kwargs=api_kwargs, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + ) + except InvalidToken as exc: + # TG returns 404 Not found for + # 1) malformed tokens + # 2) correct tokens but non-existing method, e.g. api.tg.org/botTOKEN/unkonwnMethod + # 2) is relevant only for Bot.do_api_request, that's why we have special handling for + # that here rather than in BaseRequest._request_wrapper + if self._initialized: + raise EndPointNotFound( + f"Endpoint '{camel_case_endpoint}' not found in Bot API" + ) from exc + + raise InvalidToken( + "Either the bot token was rejected by Telegram or the endpoint " + f"'{camel_case_endpoint}' does not exist." + ) from exc + + if return_type is None or isinstance(result, bool): + return result + + if isinstance(result, list): + return return_type.de_list(result, self) + return return_type.de_json(result, self) + + async def get_me( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> User: + """A simple method for testing your bot's auth token. Requires no parameters. + + Returns: + :class:`telegram.User`: A :class:`telegram.User` instance representing that bot if the + credentials are valid, :obj:`None` otherwise. + + Raises: + :class:`telegram.error.TelegramError` + + """ + result = await self._post( + "getMe", + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + self._bot_user = User.de_json(result, self) + return self._bot_user # type: ignore[return-value] + + async def send_message( + self, + chat_id: Union[int, str], + text: str, + parse_mode: ODVInput[str] = DEFAULT_NONE, + entities: Optional[Sequence["MessageEntity"]] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + message_thread_id: Optional[int] = None, + link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + disable_web_page_preview: Optional[bool] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """Use this method to send text messages. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + text (:obj:`str`): Text of the message to be sent. Max + :tg-const:`telegram.constants.MessageLimit.MAX_TEXT_LENGTH` characters after + entities parsing. + parse_mode (:obj:`str`): |parse_mode| + entities (Sequence[:class:`telegram.MessageEntity`], optional): Sequence of special + entities that appear in message text, which can be specified instead of + :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |sequenceargs| + link_preview_options (:obj:`LinkPreviewOptions`, optional): Link preview generation + options for the message. Mutually exclusive with + :paramref:`disable_web_page_preview`. + + .. versionadded:: 20.8 + + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + disable_web_page_preview (:obj:`bool`, optional): Disables link previews for links in + this message. Convenience parameter for setting :paramref:`link_preview_options`. + Mutually exclusive with :paramref:`link_preview_options`. + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`link_preview_options` replacing this + argument. PTB will automatically convert this argument to that one, but + for advanced options, please use :paramref:`link_preview_options` directly. + + .. versionchanged:: 21.0 + |keyword_only_arg| + + Returns: + :class:`telegram.Message`: On success, the sent message is returned. + + Raises: + :exc:`ValueError`: If both :paramref:`disable_web_page_preview` and + :paramref:`link_preview_options` are passed. + :class:`telegram.error.TelegramError`: For other errors. + + """ + data: JSONDict = {"chat_id": chat_id, "text": text, "entities": entities} + link_preview_options = parse_lpo_and_dwpp(disable_web_page_preview, link_preview_options) + + return await self._send_message( + "sendMessage", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + parse_mode=parse_mode, + link_preview_options=link_preview_options, + reply_parameters=reply_parameters, + message_effect_id=message_effect_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_message( + self, + chat_id: Union[str, int], + message_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to delete a message, including service messages, with the following + limitations: + + - A message can only be deleted if it was sent less than 48 hours ago. + - Service messages about a supergroup, channel, or forum topic creation can't be deleted. + - A dice message in a private chat can only be deleted if it was sent more than 24 + hours ago. + - Bots can delete outgoing messages in private chats, groups, and supergroups. + - Bots can delete incoming messages in private chats. + - Bots granted :attr:`~telegram.ChatMemberAdministrator.can_post_messages` permissions + can delete outgoing messages in channels. + - If the bot is an administrator of a group, it can delete any message there. + - If the bot has :attr:`~telegram.ChatMemberAdministrator.can_delete_messages` + permission in a supergroup or a channel, it can delete any message there. + + .. + The method CallbackQuery.delete_message() will not be found when automatically + generating "Shortcuts" admonitions for Bot methods because it has no calls + to Bot methods in its return statement(s). So it is manually included in "See also". + + .. seealso:: + :meth:`telegram.CallbackQuery.delete_message` (calls :meth:`delete_message` + indirectly, via :meth:`telegram.Message.delete`) + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + message_id (:obj:`int`): Identifier of the message to delete. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id, "message_id": message_id} + return await self._post( + "deleteMessage", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_messages( + self, + chat_id: Union[int, str], + message_ids: Sequence[int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to delete multiple messages simultaneously. If some of the specified + messages can't be found, they are skipped. + + .. versionadded:: 20.8 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + message_ids (Sequence[:obj:`int`]): A list of + :tg-const:`telegram.constants.BulkRequestLimit.MIN_LIMIT`- + :tg-const:`telegram.constants.BulkRequestLimit.MAX_LIMIT` identifiers of messages + to delete. See :meth:`delete_message` for limitations on which messages can be + deleted. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = {"chat_id": chat_id, "message_ids": message_ids} + return await self._post( + "deleteMessages", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def forward_message( + self, + chat_id: Union[int, str], + from_chat_id: Union[str, int], + message_id: int, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """ + Use this method to forward messages of any kind. Service messages can't be forwarded. + + Note: + Since the release of Bot API 5.5 it can be impossible to forward messages from + some chats. Use the attributes :attr:`telegram.Message.has_protected_content` and + :attr:`telegram.ChatFullInfo.has_protected_content` to check this. + + As a workaround, it is still possible to use :meth:`copy_message`. However, this + behaviour is undocumented and might be changed by Telegram. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + 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 + :paramref:`from_chat_id`. + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = { + "chat_id": chat_id, + "from_chat_id": from_chat_id, + "message_id": message_id, + } + + return await self._send_message( + "forwardMessage", + data, + disable_notification=disable_notification, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def forward_messages( + self, + chat_id: Union[int, str], + from_chat_id: Union[str, int], + message_ids: Sequence[int], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple[MessageId, ...]: + """ + Use this method to forward messages of any kind. If some of the specified messages can't be + found or forwarded, they are skipped. Service messages and messages with protected content + can't be forwarded. Album grouping is kept for forwarded messages. + + .. versionadded:: 20.8 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + 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_ids (Sequence[:obj:`int`]): A list of + :tg-const:`telegram.constants.BulkRequestLimit.MIN_LIMIT`- + :tg-const:`telegram.constants.BulkRequestLimit.MAX_LIMIT` identifiers of messages + in the chat :paramref:`from_chat_id` to forward. The identifiers must be specified + in a strictly increasing order. + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + Returns: + Tuple[:class:`telegram.Message`]: On success, a tuple of ``MessageId`` of sent messages + is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = { + "chat_id": chat_id, + "from_chat_id": from_chat_id, + "message_ids": message_ids, + "disable_notification": disable_notification, + "protect_content": protect_content, + "message_thread_id": message_thread_id, + } + + result = await self._post( + "forwardMessages", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + return MessageId.de_list(result, self) + + async def send_photo( + self, + chat_id: Union[int, str], + photo: Union[FileInput, "PhotoSize"], + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + has_spoiler: Optional[bool] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """Use this method to send photos. + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + photo (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` \ + | :class:`pathlib.Path` | :class:`telegram.PhotoSize`): Photo to send. + |fileinput| + Lastly you can pass an existing :class:`telegram.PhotoSize` object to send. + + Caution: + * The photo must be at most 10MB in size. + * The photo's width and height must not exceed 10000 in total. + * Width and height ratio must be at most 20. + + .. versionchanged:: 13.2 + Accept :obj:`bytes` as input. + + .. versionchanged:: 20.0 + File paths as input is also accepted for bots *not* running in + :paramref:`~telegram.Bot.local_mode`. + caption (:obj:`str`, optional): Photo caption (may also be used when resending photos + by file_id), 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` + characters after entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): + |caption_entities| + + .. versionchanged:: 20.0 + |sequenceargs| + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + has_spoiler (:obj:`bool`, optional): Pass :obj:`True` if the photo needs to be covered + with a spoiler animation. + + .. versionadded:: 20.0 + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + filename (:obj:`str`, optional): Custom file name for the photo, when uploading a + new file. Convenience parameter, useful e.g. when sending files generated by the + :obj:`tempfile` module. + + .. versionadded:: 13.1 + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "photo": self._parse_file_input(photo, PhotoSize, filename=filename), + "has_spoiler": has_spoiler, + "show_caption_above_media": show_caption_above_media, + } + + return await self._send_message( + "sendPhoto", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_audio( + self, + chat_id: Union[int, str], + audio: Union[FileInput, "Audio"], + duration: Optional[int] = None, + performer: Optional[str] = None, + title: Optional[str] = None, + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """ + Use this method to send audio files, if you want Telegram clients to display them in the + music player. Your audio must be in the ``.mp3`` or ``.m4a`` format. + + Bots can currently send audio files of up to + :tg-const:`telegram.constants.FileSizeLimit.FILESIZE_UPLOAD` in size, this limit may be + changed in the future. + + For sending voice messages, use the :meth:`send_voice` method instead. + + .. seealso:: :wiki:`Working with Files and Media ` + + .. versionchanged:: 20.5 + |removed_thumb_arg| + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + audio (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | \ + :obj:`bytes` | :class:`pathlib.Path` | :class:`telegram.Audio`): Audio file to + send. |fileinput| + Lastly you can pass an existing :class:`telegram.Audio` object to send. + + .. versionchanged:: 13.2 + Accept :obj:`bytes` as input. + + .. versionchanged:: 20.0 + File paths as input is also accepted for bots *not* running in + :paramref:`~telegram.Bot.local_mode`. + caption (:obj:`str`, optional): Audio caption, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after + entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): + |caption_entities| + + .. versionchanged:: 20.0 + |sequenceargs| + duration (:obj:`int`, optional): Duration of sent audio in seconds. + performer (:obj:`str`, optional): Performer. + title (:obj:`str`, optional): Track name. + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \ + optional): |thumbdocstring| + + .. versionadded:: 20.2 + reply_parameters (:obj:`ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + filename (:obj:`str`, optional): Custom file name for the audio, when uploading a + new file. Convenience parameter, useful e.g. when sending files generated by the + :obj:`tempfile` module. + + .. versionadded:: 13.1 + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "audio": self._parse_file_input(audio, Audio, filename=filename), + "duration": duration, + "performer": performer, + "title": title, + "thumbnail": self._parse_file_input(thumbnail, attach=True) if thumbnail else None, + } + + return await self._send_message( + "sendAudio", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_document( + self, + chat_id: Union[int, str], + document: Union[FileInput, "Document"], + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + disable_content_type_detection: Optional[bool] = None, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """ + Use this method to send general files. + + Bots can currently send files of any type of up to + :tg-const:`telegram.constants.FileSizeLimit.FILESIZE_UPLOAD` in size, this limit may be + changed in the future. + + .. seealso:: :wiki:`Working with Files and Media ` + + .. versionchanged:: 20.5 + |removed_thumb_arg| + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + document (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | \ + :obj:`bytes` | :class:`pathlib.Path` | :class:`telegram.Document`): File to send. + |fileinput| + Lastly you can pass an existing :class:`telegram.Document` object to send. + + Note: + Sending by URL will currently only work ``GIF``, ``PDF`` & ``ZIP`` files. + + .. versionchanged:: 13.2 + Accept :obj:`bytes` as input. + + .. versionchanged:: 20.0 + File paths as input is also accepted for bots *not* running in + :paramref:`~telegram.Bot.local_mode`. + caption (:obj:`str`, optional): Document caption (may also be used when resending + documents by file_id), 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` + characters after entities parsing. + disable_content_type_detection (:obj:`bool`, optional): Disables automatic server-side + content type detection for files uploaded using multipart/form-data. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): + |caption_entities| + + .. versionchanged:: 20.0 + |sequenceargs| + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \ + optional): |thumbdocstring| + + .. versionadded:: 20.2 + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + filename (:obj:`str`, optional): Custom file name for the document, when uploading a + new file. Convenience parameter, useful e.g. when sending files generated by the + :obj:`tempfile` module. + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "document": self._parse_file_input(document, Document, filename=filename), + "disable_content_type_detection": disable_content_type_detection, + "thumbnail": self._parse_file_input(thumbnail, attach=True) if thumbnail else None, + } + + return await self._send_message( + "sendDocument", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_sticker( + self, + chat_id: Union[int, str], + sticker: Union[FileInput, "Sticker"], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + emoji: Optional[str] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """ + Use this method to send static ``.WEBP``, animated ``.TGS``, or video ``.WEBM`` stickers. + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + sticker (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | \ + :obj:`bytes` | :class:`pathlib.Path` | :class:`telegram.Sticker`): Sticker to send. + |fileinput| Video stickers can only be sent by a ``file_id``. Video and animated + stickers can't be sent via an HTTP URL. + + Lastly you can pass an existing :class:`telegram.Sticker` object to send. + + .. versionchanged:: 13.2 + Accept :obj:`bytes` as input. + + .. versionchanged:: 20.0 + File paths as input is also accepted for bots *not* running in + :paramref:`~telegram.Bot.local_mode`. + emoji (:obj:`str`, optional): Emoji associated with the sticker; only for just + uploaded stickers + + .. versionadded:: 20.2 + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "sticker": self._parse_file_input(sticker, Sticker), + "emoji": emoji, + } + return await self._send_message( + "sendSticker", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_video( + self, + chat_id: Union[int, str], + video: Union[FileInput, "Video"], + duration: Optional[int] = None, + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + width: Optional[int] = None, + height: Optional[int] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + supports_streaming: Optional[bool] = None, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + has_spoiler: Optional[bool] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """Use this method to send video files, Telegram clients support mp4 videos + (other formats may be sent as Document). + + Bots can currently send video files of up to + :tg-const:`telegram.constants.FileSizeLimit.FILESIZE_UPLOAD` in size, this limit may be + changed in the future. + + Note: + :paramref:`thumbnail` will be ignored for small video files, for which Telegram can + easily generate thumbnails. However, this behaviour is undocumented and might be + changed by Telegram. + + .. seealso:: :wiki:`Working with Files and Media ` + + .. versionchanged:: 20.5 + |removed_thumb_arg| + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + video (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` \ + | :class:`pathlib.Path` | :class:`telegram.Video`): Video file to send. + |fileinput| + Lastly you can pass an existing :class:`telegram.Video` object to send. + + .. versionchanged:: 13.2 + Accept :obj:`bytes` as input. + + .. 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. + width (:obj:`int`, optional): Video width. + height (:obj:`int`, optional): Video height. + 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. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): + |caption_entities| + + .. versionchanged:: 20.0 + |sequenceargs| + supports_streaming (:obj:`bool`, optional): Pass :obj:`True`, if the uploaded video is + suitable for streaming. + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + has_spoiler (:obj:`bool`, optional): Pass :obj:`True` if the video needs to be covered + with a spoiler animation. + + .. versionadded:: 20.0 + thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \ + optional): |thumbdocstring| + + .. versionadded:: 20.2 + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + filename (:obj:`str`, optional): Custom file name for the video, when uploading a + new file. Convenience parameter, useful e.g. when sending files generated by the + :obj:`tempfile` module. + + .. versionadded:: 13.1 + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "video": self._parse_file_input(video, Video, filename=filename), + "duration": duration, + "width": width, + "height": height, + "supports_streaming": supports_streaming, + "thumbnail": self._parse_file_input(thumbnail, attach=True) if thumbnail else None, + "has_spoiler": has_spoiler, + "show_caption_above_media": show_caption_above_media, + } + + return await self._send_message( + "sendVideo", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_video_note( + self, + chat_id: Union[int, str], + video_note: Union[FileInput, "VideoNote"], + duration: Optional[int] = None, + length: Optional[int] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """ + As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. + Use this method to send video messages. + + Note: + :paramref:`thumbnail` will be ignored for small video files, for which Telegram can + easily generate thumbnails. However, this behaviour is undocumented and might be + changed by Telegram. + + .. seealso:: :wiki:`Working with Files and Media ` + + .. versionchanged:: 20.5 + |removed_thumb_arg| + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + video_note (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | \ + :obj:`bytes` | :class:`pathlib.Path` | :class:`telegram.VideoNote`): Video note + to send. + Pass a file_id as String to send a video note that exists on the Telegram + servers (recommended) or upload a new video using multipart/form-data. + |uploadinput| + Lastly you can pass an existing :class:`telegram.VideoNote` object to send. + Sending video notes by a URL is currently unsupported. + + .. versionchanged:: 13.2 + Accept :obj:`bytes` as input. + + .. 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. + length (:obj:`int`, optional): Video width and height, i.e. diameter of the video + message. + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \ + optional): |thumbdocstring| + + .. versionadded:: 20.2 + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + filename (:obj:`str`, optional): Custom file name for the video note, when uploading a + new file. Convenience parameter, useful e.g. when sending files generated by the + :obj:`tempfile` module. + + .. versionadded:: 13.1 + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "video_note": self._parse_file_input(video_note, VideoNote, filename=filename), + "duration": duration, + "length": length, + "thumbnail": self._parse_file_input(thumbnail, attach=True) if thumbnail else None, + } + + return await self._send_message( + "sendVideoNote", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_animation( + self, + chat_id: Union[int, str], + animation: Union[FileInput, "Animation"], + duration: Optional[int] = None, + width: Optional[int] = None, + height: Optional[int] = None, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + has_spoiler: Optional[bool] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """ + Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). + Bots can currently send animation files of up to + :tg-const:`telegram.constants.FileSizeLimit.FILESIZE_UPLOAD` in size, this limit may be + changed in the future. + + Note: + :paramref:`thumbnail` will be ignored for small files, for which Telegram can easily + generate thumbnails. However, this behaviour is undocumented and might be changed + by Telegram. + + .. seealso:: :wiki:`Working with Files and Media ` + + .. versionchanged:: 20.5 + |removed_thumb_arg| + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + animation (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | \ + :obj:`bytes` | :class:`pathlib.Path` | :class:`telegram.Animation`): Animation to + send. |fileinput| + Lastly you can pass an existing :class:`telegram.Animation` object to send. + + .. versionchanged:: 13.2 + Accept :obj:`bytes` as input. + duration (:obj:`int`, optional): Duration of sent animation in seconds. + width (:obj:`int`, optional): Animation width. + height (:obj:`int`, optional): Animation height. + caption (:obj:`str`, optional): Animation caption (may also be used when resending + animations by file_id), + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after + entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): + |caption_entities| + + .. versionchanged:: 20.0 + |sequenceargs| + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + has_spoiler (:obj:`bool`, optional): Pass :obj:`True` if the animation needs to be + covered with a spoiler animation. + + .. versionadded:: 20.0 + thumbnail (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \ + optional): |thumbdocstring| + + .. versionadded:: 20.2 + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + filename (:obj:`str`, optional): Custom file name for the animation, when uploading a + new file. Convenience parameter, useful e.g. when sending files generated by the + :obj:`tempfile` module. + + .. versionadded:: 13.1 + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "animation": self._parse_file_input(animation, Animation, filename=filename), + "duration": duration, + "width": width, + "height": height, + "thumbnail": self._parse_file_input(thumbnail, attach=True) if thumbnail else None, + "has_spoiler": has_spoiler, + "show_caption_above_media": show_caption_above_media, + } + + return await self._send_message( + "sendAnimation", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_voice( + self, + chat_id: Union[int, str], + voice: Union[FileInput, "Voice"], + duration: Optional[int] = None, + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """ + Use this method to send audio files, if you want Telegram clients to display the file + as a playable voice message. For this to work, your audio must be in an ``.ogg`` file + encoded with OPUS , or in .MP3 format, or in .M4A format (other formats may be sent as + :class:`~telegram.Audio` or :class:`~telegram.Document`). Bots can currently send voice + messages of up to :tg-const:`telegram.constants.FileSizeLimit.FILESIZE_UPLOAD` in size, + this limit may be changed in the future. + + Note: + To use this method, the file must have the type :mimetype:`audio/ogg` and be no more + than :tg-const:`telegram.constants.FileSizeLimit.VOICE_NOTE_FILE_SIZE` in size. + :tg-const:`telegram.constants.FileSizeLimit.VOICE_NOTE_FILE_SIZE`- + :tg-const:`telegram.constants.FileSizeLimit.FILESIZE_DOWNLOAD` voice notes will be + sent as files. + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + voice (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | :obj:`bytes` \ + | :class:`pathlib.Path` | :class:`telegram.Voice`): Voice file to send. + |fileinput| + Lastly you can pass an existing :class:`telegram.Voice` object to send. + + .. versionchanged:: 13.2 + Accept :obj:`bytes` as input. + + .. versionchanged:: 20.0 + File paths as input is also accepted for bots *not* running in + :paramref:`~telegram.Bot.local_mode`. + caption (:obj:`str`, optional): Voice message caption, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after + entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): + |caption_entities| + + .. versionchanged:: 20.0 + |sequenceargs| + duration (:obj:`int`, optional): Duration of the voice message in seconds. + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + filename (:obj:`str`, optional): Custom file name for the voice, when uploading a + new file. Convenience parameter, useful e.g. when sending files generated by the + :obj:`tempfile` module. + + .. versionadded:: 13.1 + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "voice": self._parse_file_input(voice, Voice, filename=filename), + "duration": duration, + } + + return await self._send_message( + "sendVoice", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_media_group( + self, + chat_id: Union[int, str], + media: Sequence[ + Union["InputMediaAudio", "InputMediaDocument", "InputMediaPhoto", "InputMediaVideo"] + ], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + ) -> Tuple[Message, ...]: + """Use this method to send a group of photos, videos, documents or audios as an album. + Documents and audio files can be only grouped in an album with messages of the same type. + + Note: + If you supply a :paramref:`caption` (along with either :paramref:`parse_mode` or + :paramref:`caption_entities`), then items in :paramref:`media` must have no captions, + and vice versa. + + .. seealso:: :wiki:`Working with Files and Media ` + + .. versionchanged:: 20.0 + Returns a tuple instead of a list. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + media (Sequence[:class:`telegram.InputMediaAudio`,\ + :class:`telegram.InputMediaDocument`, :class:`telegram.InputMediaPhoto`,\ + :class:`telegram.InputMediaVideo`]): An array + describing messages to be sent, must include + :tg-const:`telegram.constants.MediaGroupLimit.MIN_MEDIA_LENGTH`- + :tg-const:`telegram.constants.MediaGroupLimit.MAX_MEDIA_LENGTH` items. + + .. versionchanged:: 20.0 + |sequenceargs| + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + caption (:obj:`str`, optional): Caption that will be added to the + first element of :paramref:`media`, so that it will be used as caption for the + whole media group. + Defaults to :obj:`None`. + + .. versionadded:: 20.0 + parse_mode (:obj:`str` | :obj:`None`, optional): + Parse mode for :paramref:`caption`. + See the constants in :class:`telegram.constants.ParseMode` for the + available modes. + + .. versionadded:: 20.0 + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): + List of special entities for :paramref:`caption`, + which can be specified instead of :paramref:`parse_mode`. + Defaults to :obj:`None`. + + .. versionadded:: 20.0 + + Returns: + Tuple[:class:`telegram.Message`]: An array of the sent Messages. + + Raises: + :class:`telegram.error.TelegramError` + """ + if caption and any( + [ + any(item.caption for item in media), + any(item.caption_entities for item in media), + # if parse_mode was set explicitly, even to None, error must be raised + any(item.parse_mode is not DEFAULT_NONE for item in media), + ] + ): + raise ValueError("You can only supply either group caption or media with captions.") + + if caption: + # Copy first item (to avoid mutation of original object), apply group caption to it. + # This will lead to the group being shown with this caption. + item_to_get_caption = copy.copy(media[0]) + with item_to_get_caption._unfrozen(): + item_to_get_caption.caption = caption + if parse_mode is not DEFAULT_NONE: + item_to_get_caption.parse_mode = parse_mode + item_to_get_caption.caption_entities = parse_sequence_arg(caption_entities) + + # copy the list (just the references) to avoid mutating the original list + media = list(media) + media[0] = item_to_get_caption + + if allow_sending_without_reply is not DEFAULT_NONE and reply_parameters is not None: + raise ValueError( + "`allow_sending_without_reply` and `reply_parameters` are mutually exclusive." + ) + + if reply_to_message_id is not None and reply_parameters is not None: + raise ValueError( + "`reply_to_message_id` and `reply_parameters` are mutually exclusive." + ) + + if reply_to_message_id is not None: + reply_parameters = ReplyParameters( + message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, + ) + + data: JSONDict = { + "chat_id": chat_id, + "media": media, + "disable_notification": disable_notification, + "protect_content": protect_content, + "message_thread_id": message_thread_id, + "reply_parameters": reply_parameters, + "business_connection_id": business_connection_id, + "message_effect_id": message_effect_id, + } + + result = await self._post( + "sendMediaGroup", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + return Message.de_list(result, self) + + async def send_location( + self, + chat_id: Union[int, str], + latitude: Optional[float] = None, + longitude: Optional[float] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + live_period: Optional[int] = None, + horizontal_accuracy: Optional[float] = None, + heading: Optional[int] = None, + proximity_alert_radius: Optional[int] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + location: Optional[Location] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """Use this method to send point on the map. + + Note: + You can either supply a :paramref:`latitude` and :paramref:`longitude` or a + :paramref:`location`. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + latitude (:obj:`float`, optional): Latitude of location. + longitude (:obj:`float`, optional): Longitude of location. + 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 + 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. + 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 + :tg-const:`telegram.constants.LocationLimit.MAX_HEADING` if specified. + proximity_alert_radius (:obj:`int`, optional): For live locations, a maximum distance + for proximity alerts about approaching another chat member, in meters. Must be + between :tg-const:`telegram.constants.LocationLimit.MIN_PROXIMITY_ALERT_RADIUS` + and :tg-const:`telegram.constants.LocationLimit.MAX_PROXIMITY_ALERT_RADIUS` + if specified. + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + location (:class:`telegram.Location`, optional): The location to send. + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + if not ((latitude is not None and longitude is not None) or location): + raise ValueError( + "Either location or latitude and longitude must be passed as argument." + ) + + if not (latitude is not None or longitude is not None) ^ bool(location): + raise ValueError( + "Either location or latitude and longitude must be passed as argument. Not both." + ) + + if isinstance(location, Location): + latitude = location.latitude + longitude = location.longitude + + data: JSONDict = { + "chat_id": chat_id, + "latitude": latitude, + "longitude": longitude, + "horizontal_accuracy": horizontal_accuracy, + "live_period": live_period, + "heading": heading, + "proximity_alert_radius": proximity_alert_radius, + } + + return await self._send_message( + "sendLocation", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def edit_message_live_location( + self, + chat_id: Optional[Union[str, int]] = None, + message_id: Optional[int] = None, + inline_message_id: Optional[str] = None, + latitude: Optional[float] = None, + longitude: Optional[float] = None, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + horizontal_accuracy: Optional[float] = None, + heading: Optional[int] = None, + proximity_alert_radius: Optional[int] = None, + live_period: Optional[int] = None, + business_connection_id: Optional[str] = None, + *, + location: Optional[Location] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union[Message, bool]: + """Use this method to edit live location messages sent by the bot or via the bot + (for inline bots). A location can be edited until its :attr:`telegram.Location.live_period` + expires or editing is explicitly disabled by a call to :meth:`stop_message_live_location`. + + Note: + You can either supply a :paramref:`latitude` and :paramref:`longitude` or a + :paramref:`location`. + + Args: + chat_id (:obj:`int` | :obj:`str`, optional): Required if :paramref:`inline_message_id` + is not specified. |chat_id_channel| + message_id (:obj:`int`, optional): Required if :paramref:`inline_message_id` is not + specified. Identifier of the message to edit. + inline_message_id (:obj:`str`, optional): Required if :paramref:`chat_id` and + :paramref:`message_id` are not specified. Identifier of the inline message. + latitude (:obj:`float`, optional): Latitude of location. + longitude (:obj:`float`, optional): Longitude of location. + horizontal_accuracy (:obj:`float`, optional): The radius of uncertainty for the + location, measured in meters; + 0-:tg-const:`telegram.constants.LocationLimit.HORIZONTAL_ACCURACY`. + heading (:obj:`int`, optional): Direction in which the user is moving, in degrees. Must + be between :tg-const:`telegram.constants.LocationLimit.MIN_HEADING` + and :tg-const:`telegram.constants.LocationLimit.MAX_HEADING` if specified. + proximity_alert_radius (:obj:`int`, optional): Maximum distance for proximity alerts + about approaching another chat member, in meters. Must be between + :tg-const:`telegram.constants.LocationLimit.MIN_PROXIMITY_ALERT_RADIUS` + and :tg-const:`telegram.constants.LocationLimit.MAX_PROXIMITY_ALERT_RADIUS` + 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 + 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 + the current ``live_period`` by more than a day, and the live location expiration + date must remain within the next 90 days. If not specified, then ``live_period`` + remains unchanged + + .. versionadded:: 21.2. + business_connection_id (:obj:`str`, optional): |business_id_str_edit| + + .. versionadded:: 21.4 + + Keyword Args: + location (:class:`telegram.Location`, optional): The location to send. + + Returns: + :class:`telegram.Message`: On success, if edited message is not an inline message, the + edited message is returned, otherwise :obj:`True` is returned. + """ + # The location parameter is a convenience functionality added by us, so enforcing the + # mutual exclusivity here is nothing that Telegram would handle anyway + if not (all([latitude, longitude]) or location): + raise ValueError( + "Either location or latitude and longitude must be passed as argument." + ) + if not (latitude is not None or longitude is not None) ^ bool(location): + raise ValueError( + "Either location or latitude and longitude must be passed as argument. Not both." + ) + + if isinstance(location, Location): + latitude = location.latitude + longitude = location.longitude + + data: JSONDict = { + "latitude": latitude, + "longitude": longitude, + "chat_id": chat_id, + "message_id": message_id, + "inline_message_id": inline_message_id, + "horizontal_accuracy": horizontal_accuracy, + "heading": heading, + "proximity_alert_radius": proximity_alert_radius, + "live_period": live_period, + } + + return await self._send_message( + "editMessageLiveLocation", + data, + reply_markup=reply_markup, + business_connection_id=business_connection_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def stop_message_live_location( + self, + chat_id: Optional[Union[str, int]] = None, + message_id: Optional[int] = None, + inline_message_id: Optional[str] = None, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + business_connection_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union[Message, bool]: + """Use this method to stop updating a live location message sent by the bot or via the bot + (for inline bots) before :paramref:`~telegram.Location.live_period` expires. + + Args: + chat_id (:obj:`int` | :obj:`str`, optional): Required if :paramref:`inline_message_id` + is not specified. |chat_id_channel| + message_id (:obj:`int`, optional): Required if :paramref:`inline_message_id` is not + specified. Identifier of the sent message with live location to stop. + inline_message_id (:obj:`str`, optional): Required if :paramref:`chat_id` and + :paramref:`message_id` are not specified. Identifier of the inline message. + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): An object for a new + inline keyboard. + business_connection_id (:obj:`str`, optional): |business_id_str_edit| + + .. versionadded:: 21.4 + + Returns: + :class:`telegram.Message`: On success, if edited message is not an inline message, the + edited message is returned, otherwise :obj:`True` is returned. + """ + data: JSONDict = { + "chat_id": chat_id, + "message_id": message_id, + "inline_message_id": inline_message_id, + } + + return await self._send_message( + "stopMessageLiveLocation", + data, + reply_markup=reply_markup, + business_connection_id=business_connection_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def send_venue( + self, + chat_id: Union[int, str], + latitude: Optional[float] = None, + longitude: Optional[float] = None, + title: Optional[str] = None, + address: Optional[str] = None, + foursquare_id: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + foursquare_type: Optional[str] = None, + google_place_id: Optional[str] = None, + google_place_type: Optional[str] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + venue: Optional[Venue] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """Use this method to send information about a venue. + + Note: + * You can either supply :paramref:`venue`, or :paramref:`latitude`, + :paramref:`longitude`, :paramref:`title` and :paramref:`address` and optionally + :paramref:`foursquare_id` and :paramref:`foursquare_type` or optionally + :paramref:`google_place_id` and :paramref:`google_place_type`. + * Foursquare details and Google Place details are mutually exclusive. However, this + behaviour is undocumented and might be changed by Telegram. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + latitude (:obj:`float`, optional): Latitude of venue. + longitude (:obj:`float`, optional): Longitude of venue. + title (:obj:`str`, optional): Name of the venue. + address (:obj:`str`, optional): Address of the venue. + foursquare_id (:obj:`str`, optional): Foursquare identifier of the venue. + foursquare_type (:obj:`str`, optional): Foursquare type of the venue, if known. + (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or + "food/icecream".) + google_place_id (:obj:`str`, optional): Google Places identifier of the venue. + google_place_type (:obj:`str`, optional): Google Places type of the venue. (See + `supported types \ + `_.) + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + venue (:class:`telegram.Venue`, optional): The venue to send. + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + # The venue parameter is a convenience functionality added by us, so enforcing the + # mutual exclusivity here is nothing that Telegram would handle anyway + if not (venue or all([latitude, longitude, address, title])): + raise ValueError( + "Either venue or latitude, longitude, address and title must be " + "passed as arguments." + ) + if not bool(venue) ^ any([latitude, longitude, address, title]): + raise ValueError( + "Either venue or latitude, longitude, address and title must be " + "passed as arguments. Not both." + ) + + if isinstance(venue, Venue): + latitude = venue.location.latitude + longitude = venue.location.longitude + address = venue.address + title = venue.title + foursquare_id = venue.foursquare_id + foursquare_type = venue.foursquare_type + google_place_id = venue.google_place_id + google_place_type = venue.google_place_type + + data: JSONDict = { + "chat_id": chat_id, + "latitude": latitude, + "longitude": longitude, + "address": address, + "title": title, + "foursquare_id": foursquare_id, + "foursquare_type": foursquare_type, + "google_place_id": google_place_id, + "google_place_type": google_place_type, + } + + return await self._send_message( + "sendVenue", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_contact( + self, + chat_id: Union[int, str], + phone_number: Optional[str] = None, + first_name: Optional[str] = None, + last_name: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + vcard: Optional[str] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + contact: Optional[Contact] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """Use this method to send phone contacts. + + Note: + You can either supply :paramref:`contact` or :paramref:`phone_number` and + :paramref:`first_name` with optionally :paramref:`last_name` and optionally + :paramref:`vcard`. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + phone_number (:obj:`str`, optional): Contact's phone number. + first_name (:obj:`str`, optional): Contact's first name. + last_name (:obj:`str`, optional): Contact's last name. + vcard (:obj:`str`, optional): Additional data about the contact in the form of a vCard, + 0-:tg-const:`telegram.constants.ContactLimit.VCARD` bytes. + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + contact (:class:`telegram.Contact`, optional): The contact to send. + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + # The contact parameter is a convenience functionality added by us, so enforcing the + # mutual exclusivity here is nothing that Telegram would handle anyway + if (not contact) and (not all([phone_number, first_name])): + raise ValueError( + "Either contact or phone_number and first_name must be passed as arguments." + ) + if not bool(contact) ^ any([phone_number, first_name]): + raise ValueError( + "Either contact or phone_number and first_name must be passed as arguments. " + "Not both." + ) + + if isinstance(contact, Contact): + phone_number = contact.phone_number + first_name = contact.first_name + last_name = contact.last_name + vcard = contact.vcard + + data: JSONDict = { + "chat_id": chat_id, + "phone_number": phone_number, + "first_name": first_name, + "last_name": last_name, + "vcard": vcard, + } + + return await self._send_message( + "sendContact", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_game( + self, + chat_id: int, + game_short_name: str, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """Use this method to send a game. + + Args: + chat_id (:obj:`int`): Unique identifier for the target chat. + game_short_name (:obj:`str`): Short name of the game, serves as the unique identifier + for the game. Set up your games via `@BotFather `_. + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): An object for a new + inline keyboard. If empty, one "Play game_title" button will be + shown. If not empty, the first button must launch the game. + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id, "game_short_name": game_short_name} + + return await self._send_message( + "sendGame", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_chat_action( + self, + chat_id: Union[str, int], + action: str, + message_thread_id: Optional[int] = None, + business_connection_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method when you need to tell the user that something is happening on the bot's + side. The status is set for 5 seconds or less (when a message arrives from your bot, + Telegram clients clear its typing status). Telegram only recommends using this method when + a response from the bot will take a noticeable amount of time to arrive. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + action(:obj:`str`): Type of action to broadcast. Choose one, depending on what the user + is about to receive. For convenience look at the constants in + :class:`telegram.constants.ChatAction`. + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "action": action, + "message_thread_id": message_thread_id, + "business_connection_id": business_connection_id, + } + return await self._post( + "sendChatAction", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + def _effective_inline_results( + self, + results: Union[ + Sequence["InlineQueryResult"], Callable[[int], Optional[Sequence["InlineQueryResult"]]] + ], + next_offset: Optional[str] = None, + current_offset: Optional[str] = None, + ) -> Tuple[Sequence["InlineQueryResult"], Optional[str]]: + """ + Builds the effective results from the results input. + We make this a stand-alone method so tg.ext.ExtBot can wrap it. + + Returns: + Tuple of 1. the effective results and 2. correct the next_offset + + """ + if current_offset is not None and next_offset is not None: + raise ValueError("`current_offset` and `next_offset` are mutually exclusive!") + + if current_offset is not None: + # Convert the string input to integer + current_offset_int = 0 if not current_offset else int(current_offset) + + # for now set to empty string, stating that there are no more results + # might change later + next_offset = "" + + if callable(results): + callable_output = results(current_offset_int) + if not callable_output: + effective_results: Sequence[InlineQueryResult] = [] + else: + effective_results = callable_output + # the callback *might* return more results on the next call, so we increment + # the page count + next_offset = str(current_offset_int + 1) + + elif len(results) > (current_offset_int + 1) * InlineQueryLimit.RESULTS: + # we expect more results for the next page + next_offset_int = current_offset_int + 1 + next_offset = str(next_offset_int) + effective_results = results[ + current_offset_int + * InlineQueryLimit.RESULTS : next_offset_int + * InlineQueryLimit.RESULTS + ] + else: + effective_results = results[current_offset_int * InlineQueryLimit.RESULTS :] + else: + effective_results = results # type: ignore[assignment] + + return effective_results, next_offset + + @no_type_check # mypy doesn't play too well with hasattr + def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQueryResult": + """The reason why this method exists is similar to the description of _insert_defaults + The reason why we do this in rather than in _insert_defaults is because converting + DEFAULT_NONE to NONE *before* calling to_dict() makes it way easier to drop None entries + from the json data. + + Must return the correct object instead of editing in-place! + """ + # Copy the objects that need modification to avoid modifying the original object + copied = False + if hasattr(res, "parse_mode"): + res = copy.copy(res) + copied = True + with res._unfrozen(): + res.parse_mode = DefaultValue.get_value(res.parse_mode) + if hasattr(res, "input_message_content") and res.input_message_content: + if hasattr(res.input_message_content, "parse_mode"): + if not copied: + res = copy.copy(res) + copied = True + + with res._unfrozen(): + res.input_message_content = copy.copy(res.input_message_content) + with res.input_message_content._unfrozen(): + res.input_message_content.parse_mode = DefaultValue.get_value( + res.input_message_content.parse_mode + ) + if hasattr(res.input_message_content, "link_preview_options"): + if not copied: + res = copy.copy(res) + + with res._unfrozen(): + res.input_message_content = copy.copy(res.input_message_content) + with res.input_message_content._unfrozen(): + res.input_message_content.link_preview_options = DefaultValue.get_value( + res.input_message_content.link_preview_options + ) + + return res + + async def answer_inline_query( + self, + inline_query_id: str, + results: Union[ + Sequence["InlineQueryResult"], Callable[[int], Optional[Sequence["InlineQueryResult"]]] + ], + cache_time: Optional[int] = None, + is_personal: Optional[bool] = None, + next_offset: Optional[str] = None, + button: Optional[InlineQueryResultsButton] = None, + *, + current_offset: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to send answers to an inline query. No more than + :tg-const:`telegram.InlineQuery.MAX_RESULTS` results per query are allowed. + + Warning: + In most use cases :paramref:`current_offset` should not be passed manually. Instead of + calling this method directly, use the shortcut :meth:`telegram.InlineQuery.answer` with + :paramref:`telegram.InlineQuery.answer.auto_pagination` set to :obj:`True`, which will + take care of passing the correct value. + + .. seealso:: :wiki:`Working with Files and Media ` + + + .. versionchanged:: 20.5 + Removed deprecated arguments ``switch_pm_text`` and ``switch_pm_parameter``. + + Args: + inline_query_id (:obj:`str`): Unique identifier for the answered query. + results (List[:class:`telegram.InlineQueryResult`] | Callable): A list of results for + the inline query. In case :paramref:`current_offset` is passed, + :paramref:`results` may also be + 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 + result of the inline query may be cached on the server. Defaults to ``300``. + 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. + next_offset (:obj:`str`, optional): Pass the offset that a client should send in the + next query with the same text to receive more results. Pass an empty string if + there are no more results or if you don't support pagination. Offset length can't + exceed :tg-const:`telegram.InlineQuery.MAX_OFFSET_LENGTH` bytes. + button (:class:`telegram.InlineQueryResultsButton`, optional): A button to be shown + above the inline query results. + + .. versionadded:: 20.3 + + Keyword Args: + current_offset (:obj:`str`, optional): The :attr:`telegram.InlineQuery.offset` of + the inline query to answer. If passed, PTB will automatically take care of + the pagination for you, i.e. pass the correct :paramref:`next_offset` and truncate + the results list/get the results from the callable you passed. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + effective_results, next_offset = self._effective_inline_results( + results=results, next_offset=next_offset, current_offset=current_offset + ) + + # Apply defaults + effective_results = [ + self._insert_defaults_for_ilq_results(result) for result in effective_results + ] + + data: JSONDict = { + "inline_query_id": inline_query_id, + "results": effective_results, + "next_offset": next_offset, + "cache_time": cache_time, + "is_personal": is_personal, + "button": button, + } + + return await self._post( + "answerInlineQuery", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_user_profile_photos( + self, + user_id: int, + offset: Optional[int] = None, + limit: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> UserProfilePhotos: + """Use this method to get a list of profile pictures for a user. + + Args: + user_id (:obj:`int`): Unique identifier of the target user. + offset (:obj:`int`, optional): Sequential number of the first photo to be returned. + By default, all photos are returned. + limit (:obj:`int`, optional): Limits the number of photos to be retrieved. Values + between :tg-const:`telegram.constants.UserProfilePhotosLimit.MIN_LIMIT`- + :tg-const:`telegram.constants.UserProfilePhotosLimit.MAX_LIMIT` are accepted. + Defaults to ``100``. + + Returns: + :class:`telegram.UserProfilePhotos` + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"user_id": user_id, "offset": offset, "limit": limit} + + result = await self._post( + "getUserProfilePhotos", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + return UserProfilePhotos.de_json(result, self) # type: ignore[return-value] + + async def get_file( + self, + file_id: Union[ + str, Animation, Audio, ChatPhoto, Document, PhotoSize, Sticker, Video, VideoNote, Voice + ], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> File: + """ + Use this method to get basic info about a file and prepare it for downloading. For the + moment, bots can download files of up to + :tg-const:`telegram.constants.FileSizeLimit.FILESIZE_DOWNLOAD` in size. The file can then + be e.g. downloaded with :meth:`telegram.File.download_to_drive`. It is guaranteed that + the link will be valid for at least 1 hour. When the link expires, a new one can be + requested by calling get_file again. + + Note: + This function may not preserve the original file name and MIME type. + You should save the file's MIME type and name (if available) when the File object + is received. + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + file_id (:obj:`str` | :class:`telegram.Animation` | :class:`telegram.Audio` | \ + :class:`telegram.ChatPhoto` | :class:`telegram.Document` | \ + :class:`telegram.PhotoSize` | :class:`telegram.Sticker` | \ + :class:`telegram.Video` | :class:`telegram.VideoNote` | \ + :class:`telegram.Voice`): + Either the file identifier or an object that has a file_id attribute + to get file information about. + + Returns: + :class:`telegram.File` + + Raises: + :class:`telegram.error.TelegramError` + + """ + # Try to get the file_id from the object, if it fails, assume it's a string + with contextlib.suppress(AttributeError): + file_id = file_id.file_id # type: ignore[union-attr] + + data: JSONDict = {"file_id": file_id} + + result = await self._post( + "getFile", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + file_path = cast(dict, result).get("file_path") + 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] + + async def ban_chat_member( + self, + chat_id: Union[str, int], + user_id: int, + until_date: Optional[Union[int, datetime]] = None, + revoke_messages: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to ban a user from a group, supergroup or a channel. In the case of + supergroups and channels, the user will not be able to return to the group on their own + using invite links, etc., unless unbanned first. The bot must be an administrator in the + chat for this to work and must have the appropriate admin rights. + + .. versionadded:: 13.7 + + Args: + chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target group or username + of the target supergroup or channel (in the format ``@channelusername``). + user_id (:obj:`int`): Unique identifier of the target user. + until_date (:obj:`int` | :obj:`datetime.datetime`, optional): Date when the user will + be unbanned, unix time. If user is banned for more than 366 days or less than 30 + seconds from the current time they are considered to be banned forever. Applied + for supergroups and channels only. + 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. + revoke_messages (:obj:`bool`, optional): Pass :obj:`True` to delete all messages from + the chat for the user that is being removed. If :obj:`False`, the user will be able + to see messages in the group that were sent before the user was removed. + Always :obj:`True` for supergroups and channels. + + .. versionadded:: 13.4 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "user_id": user_id, + "revoke_messages": revoke_messages, + "until_date": until_date, + } + + return await self._post( + "banChatMember", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def ban_chat_sender_chat( + self, + chat_id: Union[str, int], + sender_chat_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to ban a channel chat in a supergroup or a channel. Until the chat is + unbanned, the owner of the banned chat won't be able to send messages on behalf of **any of + their channels**. The bot must be an administrator in the supergroup or channel for this + to work and must have the appropriate administrator rights. + + .. versionadded:: 13.9 + + Args: + chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target group or username + of the target supergroup or channel (in the format ``@channelusername``). + sender_chat_id (:obj:`int`): Unique identifier of the target sender chat. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id, "sender_chat_id": sender_chat_id} + + return await self._post( + "banChatSenderChat", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unban_chat_member( + self, + chat_id: Union[str, int], + user_id: int, + only_if_banned: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Use this method to unban a previously kicked user in a supergroup or channel. + + The user will *not* return to the group or channel automatically, but will be able to join + via link, etc. The bot must be an administrator for this to work. By default, this method + guarantees that after the call the user is not a member of the chat, but will be able to + join it. So if the user is a member of the chat they will also be *removed* from the chat. + If you don't want this, use the parameter :paramref:`only_if_banned`. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + user_id (:obj:`int`): Unique identifier of the target user. + only_if_banned (:obj:`bool`, optional): Do nothing if the user is not banned. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id, "user_id": user_id, "only_if_banned": only_if_banned} + + return await self._post( + "unbanChatMember", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unban_chat_sender_chat( + self, + chat_id: Union[str, int], + sender_chat_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Use this method to unban a previously banned channel in a supergroup or channel. + The bot must be an administrator for this to work and must have the + appropriate administrator rights. + + .. versionadded:: 13.9 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + sender_chat_id (:obj:`int`): Unique identifier of the target sender chat. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id, "sender_chat_id": sender_chat_id} + + return await self._post( + "unbanChatSenderChat", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def answer_callback_query( + self, + callback_query_id: str, + text: Optional[str] = None, + show_alert: Optional[bool] = None, + url: Optional[str] = None, + cache_time: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to send answers to callback queries sent from inline keyboards. The answer + will be displayed to the user as a notification at the top of the chat screen or as an + alert. + Alternatively, the user can be redirected to the specified Game URL. For this option to + work, you must first create a game for your bot via `@BotFather `_ + and accept the terms. Otherwise, you may use links like t.me/your_bot?start=XXXX that open + your bot with a parameter. + + Args: + callback_query_id (:obj:`str`): Unique identifier for the query to be answered. + text (:obj:`str`, optional): Text of the notification. If not specified, nothing will + be shown to the user, 0-:tg-const:`telegram.CallbackQuery.MAX_ANSWER_TEXT_LENGTH` + characters. + show_alert (:obj:`bool`, optional): If :obj:`True`, an alert will be shown by the + client instead of a notification at the top of the chat screen. Defaults to + :obj:`False`. + url (:obj:`str`, optional): URL that will be opened by the user's client. If you have + created a Game and accepted the conditions via + `@BotFather `_, specify the URL that + 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 + result of the callback query may be cached client-side. Defaults to 0. + + Returns: + :obj:`bool` On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "callback_query_id": callback_query_id, + "cache_time": cache_time, + "text": text, + "show_alert": show_alert, + "url": url, + } + + return await self._post( + "answerCallbackQuery", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def edit_message_text( + self, + text: str, + chat_id: Optional[Union[str, int]] = None, + message_id: Optional[int] = None, + inline_message_id: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + entities: Optional[Sequence["MessageEntity"]] = None, + link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, + business_connection_id: Optional[str] = None, + *, + disable_web_page_preview: Optional[bool] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union[Message, bool]: + """ + Use this method to edit text and game messages. + + Note: + * |editreplymarkup| + * |bcid_edit_time| + + .. seealso:: :attr:`telegram.Game.text` + + Args: + chat_id (:obj:`int` | :obj:`str`, optional): Required if :paramref:`inline_message_id` + is not specified. |chat_id_channel| + message_id (:obj:`int`, optional): Required if :paramref:`inline_message_id` is not + specified. Identifier of the message to edit. + inline_message_id (:obj:`str`, optional): Required if :paramref:`chat_id` and + :paramref:`message_id` are not specified. Identifier of the inline message. + text (:obj:`str`): New text of the message, + :tg-const:`telegram.constants.MessageLimit.MIN_TEXT_LENGTH`- + :tg-const:`telegram.constants.MessageLimit.MAX_TEXT_LENGTH` characters after + entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + entities (Sequence[:class:`telegram.MessageEntity`], optional): Sequence of special + entities that appear in message text, which can be specified instead of + :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |sequenceargs| + + link_preview_options (:obj:`LinkPreviewOptions`, optional): Link preview generation + options for the message. Mutually exclusive with + :paramref:`disable_web_page_preview`. + + .. versionadded:: 20.8 + + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): An object for an + inline keyboard. + business_connection_id (:obj:`str`, optional): |business_id_str_edit| + + .. versionadded:: 21.4 + + Keyword Args: + disable_web_page_preview (:obj:`bool`, optional): Disables link previews for links in + this message. Convenience parameter for setting :paramref:`link_preview_options`. + Mutually exclusive with :paramref:`link_preview_options`. + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`link_preview_options` replacing this + argument. PTB will automatically convert this argument to that one, but + for advanced options, please use :paramref:`link_preview_options` directly. + + .. versionchanged:: 21.0 + |keyword_only_arg| + + + Returns: + :class:`telegram.Message`: On success, if edited message is not an inline message, the + edited message is returned, otherwise :obj:`True` is returned. + + Raises: + :exc:`ValueError`: If both :paramref:`disable_web_page_preview` and + :paramref:`link_preview_options` are passed. + :class:`telegram.error.TelegramError`: For other errors. + + """ + data: JSONDict = { + "text": text, + "chat_id": chat_id, + "message_id": message_id, + "inline_message_id": inline_message_id, + "entities": entities, + } + + link_preview_options = parse_lpo_and_dwpp(disable_web_page_preview, link_preview_options) + + return await self._send_message( + "editMessageText", + data, + reply_markup=reply_markup, + parse_mode=parse_mode, + link_preview_options=link_preview_options, + business_connection_id=business_connection_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def edit_message_caption( + self, + chat_id: Optional[Union[str, int]] = None, + message_id: Optional[int] = None, + inline_message_id: Optional[str] = None, + caption: Optional[str] = None, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + show_caption_above_media: Optional[bool] = None, + business_connection_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union[Message, bool]: + """ + Use this method to edit captions of messages. + + Note: + * |editreplymarkup| + * |bcid_edit_time| + + Args: + chat_id (:obj:`int` | :obj:`str`, optional): Required if inline_message_id is not + specified. |chat_id_channel| + message_id (:obj:`int`, optional): Required if inline_message_id is not specified. + Identifier of the message to edit. + inline_message_id (:obj:`str`, optional): Required if chat_id and message_id are not + specified. Identifier of the inline message. + caption (:obj:`str`, optional): New caption of the message, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after + entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): + |caption_entities| + + .. versionchanged:: 20.0 + |sequenceargs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): An object for an + inline keyboard. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: 21.3 + business_connection_id (:obj:`str`, optional): |business_id_str_edit| + + .. versionadded:: 21.4 + + Returns: + :class:`telegram.Message`: On success, if edited message is not an inline message, the + edited message is returned, otherwise :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_id": message_id, + "inline_message_id": inline_message_id, + "show_caption_above_media": show_caption_above_media, + } + + return await self._send_message( + "editMessageCaption", + data, + reply_markup=reply_markup, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + business_connection_id=business_connection_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def edit_message_media( + self, + media: "InputMedia", + chat_id: Optional[Union[str, int]] = None, + message_id: Optional[int] = None, + inline_message_id: Optional[str] = None, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + business_connection_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union[Message, bool]: + """ + Use this method to edit animation, audio, document, photo, or video messages. If a message + is part of a message album, then it can be edited only to an audio for audio albums, only + to a document for document albums and to a photo or a video otherwise. When an inline + message is edited, a new file can't be uploaded; use a previously uploaded file via its + :attr:`~telegram.File.file_id` or specify a URL. + + Note: + * |editreplymarkup| + * |bcid_edit_time| + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + media (:class:`telegram.InputMedia`): An object for a new media content + of the message. + chat_id (:obj:`int` | :obj:`str`, optional): Required if inline_message_id is not + specified. |chat_id_channel| + message_id (:obj:`int`, optional): Required if inline_message_id is not specified. + Identifier of the message to edit. + inline_message_id (:obj:`str`, optional): Required if chat_id and message_id are not + specified. Identifier of the inline message. + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): An object for an + inline keyboard. + business_connection_id (:obj:`str`, optional): |business_id_str_edit| + + .. versionadded:: 21.4 + + Returns: + :class:`telegram.Message`: On success, if edited message is not an inline message, the + edited Message is returned, otherwise :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = { + "media": media, + "chat_id": chat_id, + "message_id": message_id, + "inline_message_id": inline_message_id, + } + + return await self._send_message( + "editMessageMedia", + data, + reply_markup=reply_markup, + business_connection_id=business_connection_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def edit_message_reply_markup( + self, + chat_id: Optional[Union[str, int]] = None, + message_id: Optional[int] = None, + inline_message_id: Optional[str] = None, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + business_connection_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union[Message, bool]: + """ + Use this method to edit only the reply markup of messages sent by the bot or via the bot + (for inline bots). + + Note: + * |editreplymarkup| + * |bcid_edit_time| + + Args: + chat_id (:obj:`int` | :obj:`str`, optional): Required if inline_message_id is not + specified. |chat_id_channel| + message_id (:obj:`int`, optional): Required if inline_message_id is not specified. + Identifier of the message to edit. + inline_message_id (:obj:`str`, optional): Required if chat_id and message_id are not + specified. Identifier of the inline message. + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): An object for an + inline keyboard. + business_connection_id (:obj:`str`, optional): |business_id_str_edit| + + .. versionadded:: 21.4 + + Returns: + :class:`telegram.Message`: On success, if edited message is not an inline message, the + edited message is returned, otherwise :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_id": message_id, + "inline_message_id": inline_message_id, + } + + return await self._send_message( + "editMessageReplyMarkup", + data, + reply_markup=reply_markup, + business_connection_id=business_connection_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_updates( + self, + offset: Optional[int] = None, + limit: Optional[int] = None, + timeout: Optional[int] = None, # noqa: ASYNC109 + allowed_updates: Optional[Sequence[str]] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple[Update, ...]: + """Use this method to receive incoming updates using long polling. + + Note: + 1. This method will not work if an outgoing webhook is set up. + 2. In order to avoid getting duplicate updates, recalculate offset after each + server response. + 3. To take full advantage of this library take a look at :class:`telegram.ext.Updater` + + .. seealso:: :meth:`telegram.ext.Application.run_polling`, + :meth:`telegram.ext.Updater.start_polling` + + .. versionchanged:: 20.0 + Returns a tuple instead of a list. + + Args: + offset (:obj:`int`, optional): Identifier of the first update to be returned. Must be + greater by one than the highest among the identifiers of previously received + updates. By default, updates starting with the earliest unconfirmed update are + returned. An update is considered confirmed as soon as this method is called with + an offset higher than its :attr:`telegram.Update.update_id`. The negative offset + can be specified to retrieve updates starting from -offset update from the end of + the updates queue. All previous updates will be forgotten. + limit (:obj:`int`, optional): Limits the number of updates to be retrieved. Values + between :tg-const:`telegram.constants.PollingLimit.MIN_LIMIT`- + :tg-const:`telegram.constants.PollingLimit.MAX_LIMIT` are accepted. + Defaults to ``100``. + timeout (:obj:`int`, optional): Timeout in seconds for long polling. Defaults to ``0``, + i.e. usual short polling. Should be positive, short polling should be used for + testing purposes only. + allowed_updates (Sequence[:obj:`str`]), optional): A sequence the types of + updates you want your bot to receive. For example, specify ["message", + "edited_channel_post", "callback_query"] to only receive updates of these types. + See :class:`telegram.Update` for a complete list of available update types. + Specify an empty sequence to receive all updates except + :attr:`telegram.Update.chat_member`, :attr:`telegram.Update.message_reaction` and + :attr:`telegram.Update.message_reaction_count` (default). If not specified, the + previous setting will be used. Please note that this parameter doesn't affect + updates created before the call to the get_updates, so unwanted updates may be + received for a short period of time. + + .. versionchanged:: 20.0 + |sequenceargs| + + Returns: + Tuple[:class:`telegram.Update`] + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "timeout": timeout, + "offset": offset, + "limit": limit, + "allowed_updates": allowed_updates, + } + + # The "or 0" is needed for the case where read_timeout is None. + if not isinstance(read_timeout, DefaultValue): + arg_read_timeout: float = read_timeout or 0 + else: + try: + arg_read_timeout = self._request[0].read_timeout or 0 + except NotImplementedError: + arg_read_timeout = 2 + self._warn( + PTBDeprecationWarning( + "20.7", + f"The class {self._request[0].__class__.__name__} does not override " + "the property `read_timeout`. Overriding this property will be mandatory " + "in future versions. Using 2 seconds as fallback.", + ), + stacklevel=2, + ) + + # Ideally we'd use an aggressive read timeout for the polling. However, + # * Short polling should return within 2 seconds. + # * Long polling poses a different problem: the connection might have been dropped while + # waiting for the server to return and there's no way of knowing the connection had been + # dropped in real time. + result = cast( + List[JSONDict], + await self._post( + "getUpdates", + data, + read_timeout=arg_read_timeout + timeout if timeout else arg_read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ), + ) + + if result: + self._LOGGER.debug("Getting updates: %s", [u["update_id"] for u in result]) + else: + self._LOGGER.debug("No new updates found.") + + try: + return Update.de_list(result, self) + except Exception as exc: + # This logging is in place mostly b/c we can't access the raw json data in Updater, + # where the exception is caught and logged again. Still, it might also be beneficial + # for custom usages of `get_updates`. + self._LOGGER.critical( + "Error while parsing updates! Received data was %r", result, exc_info=exc + ) + raise + + async def set_webhook( + self, + url: str, + certificate: Optional[FileInput] = None, + max_connections: Optional[int] = None, + allowed_updates: Optional[Sequence[str]] = None, + ip_address: Optional[str] = None, + drop_pending_updates: Optional[bool] = None, + secret_token: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + 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. + + 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 + ``X-Telegram-Bot-Api-Secret-Token`` with the secret token as content. + + Note: + 1. You will not be able to receive updates using :meth:`get_updates` for long as an + outgoing webhook is set up. + 2. To use a self-signed certificate, you need to upload your public key certificate + using :paramref:`certificate` parameter. Please upload as + :class:`~telegram.InputFile`, sending a String will not work. + 3. Ports currently supported for Webhooks: + :attr:`telegram.constants.SUPPORTED_WEBHOOK_PORTS`. + + If you're having any trouble setting up webhooks, please check out this `guide to + Webhooks`_. + + .. seealso:: :meth:`telegram.ext.Application.run_webhook`, + :meth:`telegram.ext.Updater.start_webhook` + + Examples: + :any:`Custom Webhook Bot ` + + Args: + url (:obj:`str`): HTTPS url to send updates to. Use an empty string to remove webhook + integration. + certificate (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`): + Upload your public key certificate so that the root + certificate in use can be checked. See our :wiki:`self-signed guide\ + ` for details. + |uploadinputnopath| + ip_address (:obj:`str`, optional): The fixed IP address which will be used to send + webhook requests instead of the IP address resolved through DNS. + max_connections (:obj:`int`, optional): Maximum allowed number of simultaneous HTTPS + connections to the webhook for update delivery, + :tg-const:`telegram.constants.WebhookLimit.MIN_CONNECTIONS_LIMIT`- + :tg-const:`telegram.constants.WebhookLimit.MAX_CONNECTIONS_LIMIT`. + Defaults to ``40``. Use lower values to limit the load on your bot's server, + and higher values to increase your bot's throughput. + allowed_updates (Sequence[:obj:`str`], optional): A sequence of the types of + updates you want your bot to receive. For example, specify ["message", + "edited_channel_post", "callback_query"] to only receive updates of these types. + See :class:`telegram.Update` for a complete list of available update types. + Specify an empty sequence to receive all updates except + :attr:`telegram.Update.chat_member`, + :attr:`telegram.Update.message_reaction` + and :attr:`telegram.Update.message_reaction_count` (default). If not + specified, the previous setting will be used. Please note that this + parameter doesn't affect + updates created before the call to the set_webhook, so unwanted update + may be received for a short period of time. + + .. versionchanged:: 20.0 + |sequenceargs| + drop_pending_updates (:obj:`bool`, optional): Pass :obj:`True` to drop all pending + updates. + secret_token (:obj:`str`, optional): A secret token to be sent in a header + ``X-Telegram-Bot-Api-Secret-Token`` in every webhook request, + :tg-const:`telegram.constants.WebhookLimit.MIN_SECRET_TOKEN_LENGTH`- + :tg-const:`telegram.constants.WebhookLimit.MAX_SECRET_TOKEN_LENGTH` characters. + Only characters ``A-Z``, ``a-z``, ``0-9``, ``_`` and ``-`` are allowed. + The header is useful to ensure that the request comes from a webhook set by you. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool` On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + .. _`guide to Webhooks`: https://core.telegram.org/bots/webhooks + + """ + data: JSONDict = { + "url": url, + "max_connections": max_connections, + "allowed_updates": allowed_updates, + "ip_address": ip_address, + "drop_pending_updates": drop_pending_updates, + "secret_token": secret_token, + "certificate": self._parse_file_input(certificate), # type: ignore[arg-type] + } + + return await self._post( + "setWebhook", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_webhook( + self, + drop_pending_updates: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to remove webhook integration if you decide to switch back to + :meth:`get_updates()`. + + Args: + drop_pending_updates (:obj:`bool`, optional): Pass :obj:`True` to drop all pending + updates. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data = {"drop_pending_updates": drop_pending_updates} + + return await self._post( + "deleteWebhook", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def leave_chat( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Use this method for your bot to leave a group, supergroup or channel. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id} + + return await self._post( + "leaveChat", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_chat( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> ChatFullInfo: + """ + Use this method to get up to date information about the chat (current name of the user for + one-on-one conversations, current username of a user, group or channel, etc.). + + .. versionchanged:: 21.2 + In accordance to Bot API 7.3, this method now returns a :class:`telegram.ChatFullInfo`. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + + Returns: + :class:`telegram.ChatFullInfo` + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id} + + result = await self._post( + "getChat", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + return ChatFullInfo.de_json(result, self) # type: ignore[return-value] + + async def get_chat_administrators( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple[ChatMember, ...]: + """ + Use this method to get a list of administrators in a chat. + + .. versionchanged:: 20.0 + Returns a tuple instead of a list. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + + Returns: + Tuple[:class:`telegram.ChatMember`]: On success, returns a tuple of ``ChatMember`` + objects that contains information about all chat administrators except + other bots. If the chat is a group or a supergroup and no administrators were + appointed, only the creator will be returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id} + result = await self._post( + "getChatAdministrators", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + return ChatMember.de_list(result, self) + + async def get_chat_member_count( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> int: + """Use this method to get the number of members in a chat. + + .. versionadded:: 13.7 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + + Returns: + :obj:`int`: Number of members in the chat. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id} + return await self._post( + "getChatMemberCount", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_chat_member( + self, + chat_id: Union[str, int], + user_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> ChatMember: + """Use this method to get information about a member of a chat. The method is only + guaranteed to work for other users if the bot is an administrator in the chat. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + user_id (:obj:`int`): Unique identifier of the target user. + + Returns: + :class:`telegram.ChatMember` + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id, "user_id": user_id} + result = await self._post( + "getChatMember", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + return ChatMember.de_json(result, self) # type: ignore[return-value] + + async def set_chat_sticker_set( + self, + chat_id: Union[str, int], + sticker_set_name: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Use this method to set a new group sticker set for a supergroup. + The bot must be an administrator in the chat for this to work and must have the appropriate + admin rights. Use the field :attr:`telegram.ChatFullInfo.can_set_sticker_set` optionally + returned in :meth:`get_chat` requests to check if the bot can use this method. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + sticker_set_name (:obj:`str`): Name of the sticker set to be set as the group + sticker set. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + data: JSONDict = {"chat_id": chat_id, "sticker_set_name": sticker_set_name} + return await self._post( + "setChatStickerSet", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_chat_sticker_set( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Use this method to delete a group sticker set from a supergroup. The bot must be an + administrator in the chat for this to work and must have the appropriate admin rights. + Use the field :attr:`telegram.ChatFullInfo.can_set_sticker_set` optionally returned in + :meth:`get_chat` requests to check if the bot can use this method. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + data: JSONDict = {"chat_id": chat_id} + return await self._post( + "deleteChatStickerSet", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_webhook_info( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> WebhookInfo: + """Use this method to get current webhook status. Requires no parameters. + + If the bot is using :meth:`get_updates`, will return an object with the + :attr:`telegram.WebhookInfo.url` field empty. + + Returns: + :class:`telegram.WebhookInfo` + + """ + result = await self._post( + "getWebhookInfo", + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + return WebhookInfo.de_json(result, self) # type: ignore[return-value] + + async def set_game_score( + self, + user_id: int, + score: int, + chat_id: Optional[int] = None, + message_id: Optional[int] = None, + inline_message_id: Optional[str] = None, + force: Optional[bool] = None, + disable_edit_message: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union[Message, bool]: + """ + Use this method to set the score of the specified user in a game message. + + .. seealso:: :attr:`telegram.Game.text` + + Args: + user_id (:obj:`int`): User identifier. + score (:obj:`int`): New score, must be non-negative. + force (:obj:`bool`, optional): Pass :obj:`True`, if the high score is allowed to + decrease. This can be useful when fixing mistakes or banning cheaters. + disable_edit_message (:obj:`bool`, optional): Pass :obj:`True`, if the game message + should not be automatically edited to include the current scoreboard. + chat_id (:obj:`int`, optional): Required if :paramref:`inline_message_id` + is not specified. Unique identifier for the target chat. + message_id (:obj:`int`, optional): Required if :paramref:`inline_message_id` is not + specified. Identifier of the sent message. + inline_message_id (:obj:`str`, optional): Required if :paramref:`chat_id` and + :paramref:`message_id` are not specified. Identifier of the inline message. + + Returns: + :class:`telegram.Message`: The edited message. If the message is not an inline message + , :obj:`True`. + + Raises: + :class:`telegram.error.TelegramError`: If the new score is not greater than the user's + current score in the chat and :paramref:`force` is :obj:`False`. + + """ + data: JSONDict = { + "user_id": user_id, + "score": score, + "force": force, + "disable_edit_message": disable_edit_message, + "chat_id": chat_id, + "message_id": message_id, + "inline_message_id": inline_message_id, + } + + return await self._send_message( + "setGameScore", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_game_high_scores( + self, + user_id: int, + chat_id: Optional[int] = None, + message_id: Optional[int] = None, + inline_message_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple[GameHighScore, ...]: + """ + Use this method to get data for high score tables. Will return the score of the specified + user and several of their neighbors in a game. + + Note: + This method will currently return scores for the target user, plus two of their + closest neighbors on each side. Will also return the top three users if the user and + his neighbors are not among them. Please note that this behavior is subject to change. + + .. versionchanged:: 20.0 + Returns a tuple instead of a list. + + Args: + user_id (:obj:`int`): Target user id. + chat_id (:obj:`int`, optional): Required if :paramref:`inline_message_id` + is not specified. Unique identifier for the target chat. + message_id (:obj:`int`, optional): Required if :paramref:`inline_message_id` is not + specified. Identifier of the sent message. + inline_message_id (:obj:`str`, optional): Required if :paramref:`chat_id` and + :paramref:`message_id` are not specified. Identifier of the inline message. + + Returns: + Tuple[:class:`telegram.GameHighScore`] + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "user_id": user_id, + "chat_id": chat_id, + "message_id": message_id, + "inline_message_id": inline_message_id, + } + + result = await self._post( + "getGameHighScores", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + return GameHighScore.de_list(result, self) + + async def send_invoice( + self, + chat_id: Union[int, str], + 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"], + start_parameter: Optional[str] = None, + photo_url: Optional[str] = None, + photo_size: Optional[int] = None, + photo_width: Optional[int] = None, + photo_height: Optional[int] = None, + need_name: Optional[bool] = None, + need_phone_number: Optional[bool] = None, + need_email: Optional[bool] = None, + need_shipping_address: Optional[bool] = None, + is_flexible: Optional[bool] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + provider_data: Optional[Union[str, object]] = None, + send_phone_number_to_provider: Optional[bool] = None, + send_email_to_provider: Optional[bool] = None, + max_tip_amount: Optional[int] = None, + suggested_tip_amounts: Optional[Sequence[int]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """Use this method to send invoices. + + Warning: + As of API 5.2 :paramref:`start_parameter` is an optional argument and therefore the + order of the arguments had to be changed. Use keyword arguments to make sure that the + arguments are passed correctly. + + .. versionchanged:: 13.5 + As of Bot API 5.2, the parameter :paramref:`start_parameter` is optional. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + title (:obj:`str`): Product name. :tg-const:`telegram.Invoice.MIN_TITLE_LENGTH`- + :tg-const:`telegram.Invoice.MAX_TITLE_LENGTH` characters. + description (:obj:`str`): Product description. + :tg-const:`telegram.Invoice.MIN_DESCRIPTION_LENGTH`- + :tg-const:`telegram.Invoice.MAX_DESCRIPTION_LENGTH` characters. + payload (:obj:`str`): Bot-defined invoice payload. + :tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`- + :tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be + displayed to the user, use it for your internal processes. + provider_token (:obj:`str`): Payments provider token, obtained via + `@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. + + currency (:obj:`str`): Three-letter ISO 4217 currency code, see `more on currencies + `_. Pass ``XTR`` for + payment in |tg_stars|. + prices (Sequence[:class:`telegram.LabeledPrice`]): Price breakdown, a sequence + of components (e.g. product price, tax, discount, delivery cost, delivery tax, + bonus, etc.). Must contain exactly one item for payment in |tg_stars|. + + .. versionchanged:: 20.0 + |sequenceargs| + max_tip_amount (:obj:`int`, optional): The maximum accepted amount for tips in the + *smallest units* of the currency (integer, **not** float/double). For example, for + a maximum tip of ``US$ 1.45`` pass ``max_tip_amount = 145``. See the ``exp`` + parameter in `currencies.json + `_, it shows the number of + digits past the decimal point for each currency (2 for the majority of currencies). + Defaults to ``0``. Not supported for payment in |tg_stars|. + + .. versionadded:: 13.5 + suggested_tip_amounts (Sequence[:obj:`int`], optional): An array of + suggested amounts of tips in the *smallest units* of the currency (integer, **not** + float/double). At most :tg-const:`telegram.Invoice.MAX_TIP_AMOUNTS` suggested tip + amounts can be specified. The suggested tip amounts must be positive, passed in a + strictly increased order and must not exceed :paramref:`max_tip_amount`. + + .. versionadded:: 13.5 + + .. versionchanged:: 20.0 + |sequenceargs| + start_parameter (:obj:`str`, optional): Unique deep-linking parameter. If left empty, + *forwarded copies* of the sent message will have a *Pay* button, allowing + multiple users to pay directly from the forwarded message, using the same invoice. + If non-empty, forwarded copies of the sent message will have a *URL* button with a + deep link to the bot (instead of a *Pay* button), with the value used as the + start parameter. + + .. versionchanged:: 13.5 + As of Bot API 5.2, this parameter is optional. + provider_data (:obj:`str` | :obj:`object`, optional): data about the + invoice, which will be shared with the payment provider. A detailed description of + required fields should be provided by the payment provider. When an object is + passed, it will be encoded as JSON. + photo_url (:obj:`str`, optional): URL of the product photo for the invoice. Can be a + photo of the goods or a marketing image for a service. People like it better when + they see what they are paying for. + photo_size (:obj:`str`, optional): Photo size. + photo_width (:obj:`int`, optional): Photo width. + photo_height (:obj:`int`, optional): Photo height. + need_name (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's full + name to complete the order. Ignored for payments in |tg_stars|. + need_phone_number (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's + phone number to complete the order. Ignored for payments in |tg_stars|. + need_email (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's email + to complete the order. Ignored for payments in |tg_stars|. + need_shipping_address (:obj:`bool`, optional): Pass :obj:`True`, if you require the + user's shipping address to complete the order. Ignored for payments in + |tg_stars|. + send_phone_number_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's + phone number should be sent to provider. Ignored for payments in |tg_stars|. + send_email_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's email + address should be sent to provider. Ignored for payments in |tg_stars|. + is_flexible (:obj:`bool`, optional): Pass :obj:`True`, if the final price depends on + the shipping method. Ignored for payments in |tg_stars|. + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): An object for an + inline keyboard. If empty, one 'Pay total price' button will be + shown. If not empty, the first button must be a Pay button. + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "title": title, + "description": description, + "payload": payload, + "provider_token": provider_token, + "currency": currency, + "prices": prices, + "max_tip_amount": max_tip_amount, + "suggested_tip_amounts": suggested_tip_amounts, + "start_parameter": start_parameter, + "provider_data": provider_data, + "photo_url": photo_url, + "photo_size": photo_size, + "photo_width": photo_width, + "photo_height": photo_height, + "need_name": need_name, + "need_phone_number": need_phone_number, + "need_email": need_email, + "need_shipping_address": need_shipping_address, + "is_flexible": is_flexible, + "send_phone_number_to_provider": send_phone_number_to_provider, + "send_email_to_provider": send_email_to_provider, + } + + return await self._send_message( + "sendInvoice", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + message_effect_id=message_effect_id, + ) + + async def answer_shipping_query( + self, + shipping_query_id: str, + ok: bool, + shipping_options: Optional[Sequence["ShippingOption"]] = None, + error_message: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + If you sent an invoice requesting a shipping address and the parameter + :paramref:`send_invoice.is_flexible` was specified, the Bot API will send an + :class:`telegram.Update` with a :attr:`telegram.Update.shipping_query` field to the bot. + Use this method to reply to shipping queries. + + Args: + shipping_query_id (:obj:`str`): Unique identifier for the query to be answered. + ok (:obj:`bool`): Specify :obj:`True` if delivery to the specified address is possible + and :obj:`False` if there are any problems (for example, if delivery to the + specified address is not possible). + shipping_options (Sequence[:class:`telegram.ShippingOption`]), optional): Required if + :paramref:`ok` is :obj:`True`. A sequence of available shipping options. + + .. versionchanged:: 20.0 + |sequenceargs| + error_message (:obj:`str`, optional): Required if :paramref:`ok` is :obj:`False`. + Error message in human readable form that explains why it is impossible to complete + the order (e.g. "Sorry, delivery to your desired address is unavailable"). Telegram + will display this message to the user. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "shipping_query_id": shipping_query_id, + "ok": ok, + "shipping_options": shipping_options, + "error_message": error_message, + } + + return await self._post( + "answerShippingQuery", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def answer_pre_checkout_query( + self, + pre_checkout_query_id: str, + ok: bool, + error_message: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Once the user has confirmed their payment and shipping details, the Bot API sends the final + confirmation in the form of an :class:`telegram.Update` with the field + :attr:`telegram.Update.pre_checkout_query`. Use this method to respond to such pre-checkout + queries. + + Note: + The Bot API must receive an answer within 10 seconds after the pre-checkout + query was sent. + + Args: + pre_checkout_query_id (:obj:`str`): Unique identifier for the query to be answered. + ok (:obj:`bool`): Specify :obj:`True` if everything is alright + (goods are available, etc.) and the bot is ready to proceed with the order. Use + :obj:`False` if there are any problems. + error_message (:obj:`str`, optional): Required if :paramref:`ok` is :obj:`False`. Error + message in human readable form that explains the reason for failure to proceed with + the checkout (e.g. "Sorry, somebody just bought the last of our amazing black + T-shirts while you were busy filling out your payment details. Please choose a + different color or garment!"). Telegram will display this message to the user. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "pre_checkout_query_id": pre_checkout_query_id, + "ok": ok, + "error_message": error_message, + } + + return await self._post( + "answerPreCheckoutQuery", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def answer_web_app_query( + self, + web_app_query_id: str, + result: "InlineQueryResult", + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> SentWebAppMessage: + """Use this method to set the result of an interaction with a Web App and send a + corresponding message on behalf of the user to the chat from which the query originated. + + .. versionadded:: 20.0 + + Args: + web_app_query_id (:obj:`str`): Unique identifier for the query to be answered. + result (:class:`telegram.InlineQueryResult`): An object describing the message to be + sent. + + Returns: + :class:`telegram.SentWebAppMessage`: On success, a sent + :class:`telegram.SentWebAppMessage` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "web_app_query_id": web_app_query_id, + "result": self._insert_defaults_for_ilq_results(result), + } + + api_result = await self._post( + "answerWebAppQuery", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + return SentWebAppMessage.de_json(api_result, self) # type: ignore[return-value] + + async def restrict_chat_member( + self, + chat_id: Union[str, int], + user_id: int, + permissions: ChatPermissions, + until_date: Optional[Union[int, datetime]] = None, + use_independent_chat_permissions: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to restrict a user in a supergroup. The bot must be an administrator in + the supergroup for this to work and must have the appropriate admin rights. Pass + :obj:`True` for all boolean parameters to lift restrictions from a user. + + .. seealso:: :meth:`telegram.ChatPermissions.all_permissions` + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + user_id (:obj:`int`): Unique identifier of the target user. + until_date (:obj:`int` | :obj:`datetime.datetime`, optional): Date when restrictions + will be lifted for the user, unix time. If user is restricted for more than 366 + days or less than 30 seconds from the current time, they are considered to be + restricted forever. + 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. + permissions (:class:`telegram.ChatPermissions`): An object for new user + permissions. + use_independent_chat_permissions (:obj:`bool`, optional): Pass :obj:`True` if chat + permissions are set independently. Otherwise, the + :attr:`~telegram.ChatPermissions.can_send_other_messages` and + :attr:`~telegram.ChatPermissions.can_add_web_page_previews` permissions will imply + the :attr:`~telegram.ChatPermissions.can_send_messages`, + :attr:`~telegram.ChatPermissions.can_send_audios`, + :attr:`~telegram.ChatPermissions.can_send_documents`, + :attr:`~telegram.ChatPermissions.can_send_photos`, + :attr:`~telegram.ChatPermissions.can_send_videos`, + :attr:`~telegram.ChatPermissions.can_send_video_notes`, and + :attr:`~telegram.ChatPermissions.can_send_voice_notes` permissions; the + :attr:`~telegram.ChatPermissions.can_send_polls` permission will imply the + :attr:`~telegram.ChatPermissions.can_send_messages` permission. + + .. versionadded: 20.1 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = { + "chat_id": chat_id, + "user_id": user_id, + "permissions": permissions, + "until_date": until_date, + "use_independent_chat_permissions": use_independent_chat_permissions, + } + + return await self._post( + "restrictChatMember", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def promote_chat_member( + self, + chat_id: Union[str, int], + user_id: int, + can_change_info: Optional[bool] = None, + can_post_messages: Optional[bool] = None, + can_edit_messages: Optional[bool] = None, + can_delete_messages: Optional[bool] = None, + can_invite_users: Optional[bool] = None, + can_restrict_members: Optional[bool] = None, + can_pin_messages: Optional[bool] = None, + can_promote_members: Optional[bool] = None, + is_anonymous: Optional[bool] = None, + can_manage_chat: Optional[bool] = None, + can_manage_video_chats: Optional[bool] = None, + can_manage_topics: Optional[bool] = None, + can_post_stories: Optional[bool] = None, + can_edit_stories: Optional[bool] = None, + can_delete_stories: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to promote or demote a user in a supergroup or a channel. The bot must be + an administrator in the chat for this to work and must have the appropriate admin rights. + Pass :obj:`False` for all boolean parameters to demote a user. + + .. versionchanged:: 20.0 + The argument ``can_manage_voice_chats`` was renamed to + :paramref:`can_manage_video_chats` in accordance to Bot API 6.0. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + user_id (:obj:`int`): Unique identifier of the target user. + is_anonymous (:obj:`bool`, optional): Pass :obj:`True`, if the administrator's presence + in the chat is hidden. + can_manage_chat (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can + access the chat event log, get boost list, see hidden supergroup and channel + members, report spam messages and ignore slow mode. Implied by any other + administrator privilege. + + .. versionadded:: 13.4 + + can_manage_video_chats (:obj:`bool`, optional): Pass :obj:`True`, if the administrator + can manage video chats. + + .. versionadded:: 20.0 + + can_change_info (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can + change chat title, photo and other settings. + can_post_messages (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can + post messages in the channel, or access channel statistics; for channels only. + can_edit_messages (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can + edit messages of other users and can pin messages, for channels only. + can_delete_messages (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can + delete messages of other users. + can_invite_users (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can + invite new users to the chat. + can_restrict_members (:obj:`bool`, optional): Pass :obj:`True`, if the administrator + can restrict, ban or unban chat members, or access supergroup statistics. + can_pin_messages (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can + pin messages, for supergroups only. + can_promote_members (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can + add new administrators with a subset of their own privileges or demote + administrators that they have promoted, directly or indirectly + (promoted by administrators that were appointed by the user). + can_manage_topics (:obj:`bool`, optional): Pass :obj:`True`, if the user is + allowed to create, rename, close, and reopen forum topics; for supergroups only. + + .. versionadded:: 20.0 + can_post_stories (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can + post stories to the chat. + + .. versionadded:: 20.6 + can_edit_stories (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can + edit stories posted by other users, post stories to the chat page, pin chat + stories, and access the chat's story archive + + .. versionadded:: 20.6 + can_delete_stories (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can + delete stories posted by other users. + + .. versionadded:: 20.6 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "user_id": user_id, + "is_anonymous": is_anonymous, + "can_change_info": can_change_info, + "can_post_messages": can_post_messages, + "can_edit_messages": can_edit_messages, + "can_delete_messages": can_delete_messages, + "can_invite_users": can_invite_users, + "can_restrict_members": can_restrict_members, + "can_pin_messages": can_pin_messages, + "can_promote_members": can_promote_members, + "can_manage_chat": can_manage_chat, + "can_manage_video_chats": can_manage_video_chats, + "can_manage_topics": can_manage_topics, + "can_post_stories": can_post_stories, + "can_edit_stories": can_edit_stories, + "can_delete_stories": can_delete_stories, + } + + return await self._post( + "promoteChatMember", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_chat_permissions( + self, + chat_id: Union[str, int], + permissions: ChatPermissions, + use_independent_chat_permissions: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to set default chat permissions for all members. The bot must be an + administrator in the group or a supergroup for this to work and must have the + :attr:`telegram.ChatMemberAdministrator.can_restrict_members` admin rights. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + permissions (:class:`telegram.ChatPermissions`): New default chat permissions. + use_independent_chat_permissions (:obj:`bool`, optional): Pass :obj:`True` if chat + permissions are set independently. Otherwise, the + :attr:`~telegram.ChatPermissions.can_send_other_messages` and + :attr:`~telegram.ChatPermissions.can_add_web_page_previews` permissions will imply + the :attr:`~telegram.ChatPermissions.can_send_messages`, + :attr:`~telegram.ChatPermissions.can_send_audios`, + :attr:`~telegram.ChatPermissions.can_send_documents`, + :attr:`~telegram.ChatPermissions.can_send_photos`, + :attr:`~telegram.ChatPermissions.can_send_videos`, + :attr:`~telegram.ChatPermissions.can_send_video_notes`, and + :attr:`~telegram.ChatPermissions.can_send_voice_notes` permissions; the + :attr:`~telegram.ChatPermissions.can_send_polls` permission will imply the + :attr:`~telegram.ChatPermissions.can_send_messages` permission. + + .. versionadded: 20.1 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "permissions": permissions, + "use_independent_chat_permissions": use_independent_chat_permissions, + } + return await self._post( + "setChatPermissions", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_chat_administrator_custom_title( + self, + chat_id: Union[int, str], + user_id: int, + custom_title: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to set a custom title for administrators promoted by the bot in a + supergroup. The bot must be an administrator for this to work. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + user_id (:obj:`int`): Unique identifier of the target administrator. + custom_title (:obj:`str`): New custom title for the administrator; + 0-:tg-const:`telegram.constants.ChatLimit.CHAT_ADMINISTRATOR_CUSTOM_TITLE_LENGTH` + characters, emoji are not allowed. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id, "user_id": user_id, "custom_title": custom_title} + + return await self._post( + "setChatAdministratorCustomTitle", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def export_chat_invite_link( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> str: + """ + Use this method to generate a new primary invite link for a chat; any previously generated + link is revoked. The bot must be an administrator in the chat for this to work and must + have the appropriate admin rights. + + Note: + Each administrator in a chat generates their own invite links. Bots can't use invite + links generated by other administrators. If you want your bot to work with invite + links, it will need to generate its own link using :meth:`export_chat_invite_link` or + by calling the :meth:`get_chat` method. If your bot needs to generate a new primary + invite link replacing its previous one, use :meth:`export_chat_invite_link` again. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + + Returns: + :obj:`str`: New invite link on success. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id} + return await self._post( + "exportChatInviteLink", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def create_chat_invite_link( + self, + chat_id: Union[str, int], + expire_date: Optional[Union[int, datetime]] = None, + member_limit: Optional[int] = None, + name: Optional[str] = None, + creates_join_request: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> ChatInviteLink: + """ + Use this method to create an additional invite link for a chat. The bot must be an + administrator in the chat for this to work and must have the appropriate admin rights. + The link can be revoked using the method :meth:`revoke_chat_invite_link`. + + Note: + When joining *public* groups via an invite link, Telegram clients may display the + usual "Join" button, effectively ignoring the invite link. In particular, the parameter + :paramref:`creates_join_request` has no effect in this case. + However, this behavior is undocument and may be subject to change. + See `this GitHub thread `_ + for some discussion. + + .. versionadded:: 13.4 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + expire_date (:obj:`int` | :obj:`datetime.datetime`, optional): Date when the link will + expire. Integer input will be interpreted as Unix timestamp. + 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. + member_limit (:obj:`int`, optional): Maximum number of users that can be members of + the chat simultaneously after joining the chat via this invite link; + :tg-const:`telegram.constants.ChatInviteLinkLimit.MIN_MEMBER_LIMIT`- + :tg-const:`telegram.constants.ChatInviteLinkLimit.MAX_MEMBER_LIMIT`. + name (:obj:`str`, optional): Invite link name; + 0-:tg-const:`telegram.constants.ChatInviteLinkLimit.NAME_LENGTH` characters. + + .. versionadded:: 13.8 + creates_join_request (:obj:`bool`, optional): :obj:`True`, if users joining the chat + via the link need to be approved by chat administrators. + If :obj:`True`, :paramref:`member_limit` can't be specified. + + .. versionadded:: 13.8 + + Returns: + :class:`telegram.ChatInviteLink` + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "expire_date": expire_date, + "member_limit": member_limit, + "name": name, + "creates_join_request": creates_join_request, + } + + result = await self._post( + "createChatInviteLink", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + return ChatInviteLink.de_json(result, self) # type: ignore[return-value] + + async def edit_chat_invite_link( + self, + chat_id: Union[str, int], + invite_link: Union[str, "ChatInviteLink"], + expire_date: Optional[Union[int, datetime]] = None, + member_limit: Optional[int] = None, + name: Optional[str] = None, + creates_join_request: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> ChatInviteLink: + """ + Use this method to edit a non-primary invite link created by the bot. The bot must be an + administrator in the chat for this to work and must have the appropriate admin rights. + + Note: + Though not stated explicitly in the official docs, Telegram changes not only the + optional parameters that are explicitly passed, but also replaces all other optional + parameters to the default values. However, since not documented, this behaviour may + change unbeknown to PTB. + + .. versionadded:: 13.4 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + invite_link (:obj:`str` | :class:`telegram.ChatInviteLink`): The invite link to edit. + + .. versionchanged:: 20.0 + Now also accepts :class:`telegram.ChatInviteLink` instances. + expire_date (:obj:`int` | :obj:`datetime.datetime`, optional): Date when the link will + expire. + For timezone naive :obj:`datetime.datetime` objects, the default timezone of the + bot will be used, which is UTC unless :attr:`telegram.ext.Defaults.tzinfo` is + used. + member_limit (:obj:`int`, optional): Maximum number of users that can be members of + the chat simultaneously after joining the chat via this invite link; + :tg-const:`telegram.constants.ChatInviteLinkLimit.MIN_MEMBER_LIMIT`- + :tg-const:`telegram.constants.ChatInviteLinkLimit.MAX_MEMBER_LIMIT`. + name (:obj:`str`, optional): Invite link name; + 0-:tg-const:`telegram.constants.ChatInviteLinkLimit.NAME_LENGTH` characters. + + .. versionadded:: 13.8 + creates_join_request (:obj:`bool`, optional): :obj:`True`, if users joining the chat + via the link need to be approved by chat administrators. + If :obj:`True`, :paramref:`member_limit` can't be specified. + + .. versionadded:: 13.8 + + Returns: + :class:`telegram.ChatInviteLink` + + Raises: + :class:`telegram.error.TelegramError` + + """ + link = invite_link.invite_link if isinstance(invite_link, ChatInviteLink) else invite_link + data: JSONDict = { + "chat_id": chat_id, + "invite_link": link, + "expire_date": expire_date, + "member_limit": member_limit, + "name": name, + "creates_join_request": creates_join_request, + } + + result = await self._post( + "editChatInviteLink", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + return ChatInviteLink.de_json(result, self) # type: ignore[return-value] + + async def revoke_chat_invite_link( + self, + chat_id: Union[str, int], + invite_link: Union[str, "ChatInviteLink"], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> ChatInviteLink: + """ + Use this method to revoke an invite link created by the bot. If the primary link is + revoked, a new link is automatically generated. The bot must be an administrator in the + chat for this to work and must have the appropriate admin rights. + + .. versionadded:: 13.4 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + invite_link (:obj:`str` | :class:`telegram.ChatInviteLink`): The invite link to revoke. + + .. versionchanged:: 20.0 + Now also accepts :class:`telegram.ChatInviteLink` instances. + + Returns: + :class:`telegram.ChatInviteLink` + + Raises: + :class:`telegram.error.TelegramError` + + """ + link = invite_link.invite_link if isinstance(invite_link, ChatInviteLink) else invite_link + data: JSONDict = {"chat_id": chat_id, "invite_link": link} + + result = await self._post( + "revokeChatInviteLink", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + return ChatInviteLink.de_json(result, self) # type: ignore[return-value] + + async def approve_chat_join_request( + self, + chat_id: Union[str, int], + user_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Use this method to approve a chat join request. + + The bot must be an administrator in the chat for this to work and must have the + :attr:`telegram.ChatPermissions.can_invite_users` administrator right. + + .. versionadded:: 13.8 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + user_id (:obj:`int`): Unique identifier of the target user. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = {"chat_id": chat_id, "user_id": user_id} + + return await self._post( + "approveChatJoinRequest", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def decline_chat_join_request( + self, + chat_id: Union[str, int], + user_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Use this method to decline a chat join request. + + The bot must be an administrator in the chat for this to work and must have the + :attr:`telegram.ChatPermissions.can_invite_users` administrator right. + + .. versionadded:: 13.8 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + user_id (:obj:`int`): Unique identifier of the target user. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = {"chat_id": chat_id, "user_id": user_id} + + return await self._post( + "declineChatJoinRequest", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_chat_photo( + self, + chat_id: Union[str, int], + photo: FileInput, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Use this method to set a new profile photo for the chat. + + Photos can't be changed for private chats. The bot must be an administrator in the chat + for this to work and must have the appropriate admin rights. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + photo (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path`): New chat photo. + |uploadinput| + + .. versionchanged:: 13.2 + Accept :obj:`bytes` as input. + + .. versionchanged:: 20.0 + File paths as input is also accepted for bots *not* running in + :paramref:`~telegram.Bot.local_mode`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id, "photo": self._parse_file_input(photo)} + return await self._post( + "setChatPhoto", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_chat_photo( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to delete a chat photo. Photos can't be changed for private chats. The bot + must be an administrator in the chat for this to work and must have the appropriate admin + rights. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id} + return await self._post( + "deleteChatPhoto", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_chat_title( + self, + chat_id: Union[str, int], + title: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to change the title of a chat. Titles can't be changed for private chats. + The bot must be an administrator in the chat for this to work and must have the appropriate + admin rights. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + title (:obj:`str`): New chat title, + :tg-const:`telegram.constants.ChatLimit.MIN_CHAT_TITLE_LENGTH`- + :tg-const:`telegram.constants.ChatLimit.MAX_CHAT_TITLE_LENGTH` characters. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id, "title": title} + return await self._post( + "setChatTitle", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_chat_description( + self, + chat_id: Union[str, int], + description: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to change the description of a group, a supergroup or a channel. The bot + must be an administrator in the chat for this to work and must have the appropriate admin + rights. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + description (:obj:`str`, optional): New chat description, + 0-:tg-const:`telegram.constants.ChatLimit.CHAT_DESCRIPTION_LENGTH` + characters. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id, "description": description} + + return await self._post( + "setChatDescription", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def pin_chat_message( + self, + chat_id: Union[str, int], + message_id: int, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + business_connection_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to add a message to the list of pinned messages in a chat. If the + chat is not a private chat, the bot must be an administrator in the chat for this to work + and must have the :paramref:`~telegram.ChatAdministratorRights.can_pin_messages` admin + right in a supergroup or :attr:`~telegram.ChatMemberAdministrator.can_edit_messages` admin + right in a channel. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + message_id (:obj:`int`): Identifier of a message to pin. + disable_notification (:obj:`bool`, optional): Pass :obj:`True`, if it is not necessary + to send a notification to all chat members about the new pinned message. + Notifications are always disabled in channels and private chats. + business_connection_id (:obj:`str`, optional): Unique identifier of the business + connection on behalf of which the message will be pinned. + + .. versionadded:: 21.5 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_id": message_id, + "disable_notification": disable_notification, + "business_connection_id": business_connection_id, + } + + return await self._post( + "pinChatMessage", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unpin_chat_message( + self, + chat_id: Union[str, int], + message_id: Optional[int] = None, + business_connection_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to remove a message from the list of pinned messages in a chat. If the + chat is not a private chat, the bot must be an administrator in the chat for this to work + and must have the :paramref:`~telegram.ChatAdministratorRights.can_pin_messages` admin + right in a supergroup or :attr:`~telegram.ChatMemberAdministrator.can_edit_messages` admin + right in a channel. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + message_id (:obj:`int`, optional): Identifier of the message to unpin. Required if + :paramref:`business_connection_id` is specified. If not specified, + the most recent pinned message (by sending date) will be unpinned. + business_connection_id (:obj:`str`, optional): Unique identifier of the business + connection on behalf of which the message will be unpinned. + + .. versionadded:: 21.5 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_id": message_id, + "business_connection_id": business_connection_id, + } + + return await self._post( + "unpinChatMessage", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unpin_all_chat_messages( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to clear the list of pinned messages in a chat. If the + chat is not a private chat, the bot must be an administrator in the chat for this + to work and must have the :paramref:`~telegram.ChatAdministratorRights.can_pin_messages` + admin right in a supergroup or :attr:`~telegram.ChatMemberAdministrator.can_edit_messages` + admin right in a channel. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id} + return await self._post( + "unpinAllChatMessages", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_sticker_set( + self, + name: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> StickerSet: + """Use this method to get a sticker set. + + Args: + name (:obj:`str`): Name of the sticker set. + + Returns: + :class:`telegram.StickerSet` + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"name": name} + result = await self._post( + "getStickerSet", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + return StickerSet.de_json(result, self) # type: ignore[return-value] + + async def get_custom_emoji_stickers( + self, + custom_emoji_ids: Sequence[str], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple[Sticker, ...]: + """ + Use this method to get information about emoji stickers by their identifiers. + + .. versionchanged:: 20.0 + Returns a tuple instead of a list. + + Args: + custom_emoji_ids (Sequence[:obj:`str`]): Sequence of custom emoji identifiers. + At most :tg-const:`telegram.constants.CustomEmojiStickerLimit.\ +CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. + + .. versionchanged:: 20.0 + |sequenceargs| + + Returns: + Tuple[:class:`telegram.Sticker`] + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"custom_emoji_ids": custom_emoji_ids} + result = await self._post( + "getCustomEmojiStickers", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + return Sticker.de_list(result, self) + + async def upload_sticker_file( + self, + user_id: int, + sticker: FileInput, + sticker_format: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> File: + """ + Use this method to upload a file with a sticker for later use in the + :meth:`create_new_sticker_set` and :meth:`add_sticker_to_set` methods (can be used multiple + times). + + .. versionchanged:: 20.5 + Removed deprecated parameter ``png_sticker``. + + Args: + user_id (:obj:`int`): User identifier of sticker file owner. + sticker (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | \ + :obj:`bytes` | :class:`pathlib.Path`): A file with the sticker in the + ``".WEBP"``, ``".PNG"``, ``".TGS"`` or ``".WEBM"`` + format. See `here `_ for technical requirements + . |uploadinput| + + .. versionadded:: 20.2 + + sticker_format (:obj:`str`): Format of the sticker. Must be one of + :attr:`telegram.constants.StickerFormat.STATIC`, + :attr:`telegram.constants.StickerFormat.ANIMATED`, + :attr:`telegram.constants.StickerFormat.VIDEO`. + + .. versionadded:: 20.2 + + Returns: + :class:`telegram.File`: On success, the uploaded File is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "user_id": user_id, + "sticker": self._parse_file_input(sticker), + "sticker_format": sticker_format, + } + result = await self._post( + "uploadStickerFile", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + return File.de_json(result, self) # type: ignore[return-value] + + async def add_sticker_to_set( + self, + user_id: int, + name: str, + sticker: "InputSticker", + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to add a new sticker to a set created by the bot. The format of the added + sticker must match the format of the other stickers in the set. Emoji sticker sets can have + up to :tg-const:`telegram.constants.StickerSetLimit.MAX_EMOJI_STICKERS` stickers. Other + sticker sets can have up to + :tg-const:`telegram.constants.StickerSetLimit.MAX_STATIC_STICKERS` stickers. + + .. versionchanged:: 20.2 + Since Bot API 6.6, the parameter :paramref:`sticker` replace the parameters + ``png_sticker``, ``tgs_sticker``, ``webm_sticker``, ``emojis``, and ``mask_position``. + + .. versionchanged:: 20.5 + Removed deprecated parameters ``png_sticker``, ``tgs_sticker``, ``webm_sticker``, + ``emojis``, and ``mask_position``. + + Args: + user_id (:obj:`int`): User identifier of created sticker set owner. + name (:obj:`str`): Sticker set name. + sticker (:class:`telegram.InputSticker`): An object with information about the added + sticker. If exactly the same sticker had already been added to the set, then the + set isn't changed. + + .. versionadded:: 20.2 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "user_id": user_id, + "name": name, + "sticker": sticker, + } + + return await self._post( + "addStickerToSet", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_sticker_position_in_set( + self, + sticker: str, + position: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Use this method to move a sticker in a set created by the bot to a specific position. + + Args: + sticker (:obj:`str`): File identifier of the sticker. + position (:obj:`int`): New sticker position in the set, zero-based. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"sticker": sticker, "position": position} + return await self._post( + "setStickerPositionInSet", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def create_new_sticker_set( + self, + user_id: int, + name: str, + title: str, + stickers: Sequence["InputSticker"], + sticker_type: Optional[str] = None, + needs_repainting: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to create new sticker set owned by a user. + The bot will be able to edit the created sticker set thus created. + + .. versionchanged:: 20.0 + The parameter ``contains_masks`` has been removed. Use :paramref:`sticker_type` + instead. + + .. versionchanged:: 20.2 + Since Bot API 6.6, the parameters :paramref:`stickers` and :paramref:`sticker_format` + replace the parameters ``png_sticker``, ``tgs_sticker``,``webm_sticker``, ``emojis``, + and ``mask_position``. + + .. versionchanged:: 20.5 + Removed the deprecated parameters mentioned above and adjusted the order of the + parameters. + + .. versionremoved:: 21.2 + Removed the deprecated parameter ``sticker_format``. + + Args: + user_id (:obj:`int`): User identifier of created sticker set owner. + name (:obj:`str`): Short name of sticker set, to be used in t.me/addstickers/ URLs + (e.g., animals). Can contain only english letters, digits and underscores. + Must begin with a letter, can't contain consecutive underscores and + must end in "_by_". is case insensitive. + :tg-const:`telegram.constants.StickerLimit.MIN_NAME_AND_TITLE`- + :tg-const:`telegram.constants.StickerLimit.MAX_NAME_AND_TITLE` characters. + title (:obj:`str`): Sticker set title, + :tg-const:`telegram.constants.StickerLimit.MIN_NAME_AND_TITLE`- + :tg-const:`telegram.constants.StickerLimit.MAX_NAME_AND_TITLE` characters. + + stickers (Sequence[:class:`telegram.InputSticker`]): A sequence of + :tg-const:`telegram.constants.StickerSetLimit.MIN_INITIAL_STICKERS`- + :tg-const:`telegram.constants.StickerSetLimit.MAX_INITIAL_STICKERS` initial + stickers to be added to the sticker set. + + .. versionadded:: 20.2 + + sticker_type (:obj:`str`, optional): Type of stickers in the set, pass + :attr:`telegram.Sticker.REGULAR` or :attr:`telegram.Sticker.MASK`, or + :attr:`telegram.Sticker.CUSTOM_EMOJI`. By default, a regular sticker set is created + + .. versionadded:: 20.0 + + needs_repainting (:obj:`bool`, optional): Pass :obj:`True` if stickers in the sticker + set must be repainted to the color of text when used in messages, the accent color + if used as emoji status, white on chat photos, or another appropriate color based + on context; for custom emoji sticker sets only. + + .. versionadded:: 20.2 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = { + "user_id": user_id, + "name": name, + "title": title, + "stickers": stickers, + "sticker_type": sticker_type, + "needs_repainting": needs_repainting, + } + + return await self._post( + "createNewStickerSet", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_sticker_from_set( + self, + sticker: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Use this method to delete a sticker from a set created by the bot. + + Args: + sticker (:obj:`str`): File identifier of the sticker. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"sticker": sticker} + return await self._post( + "deleteStickerFromSet", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_sticker_set( + self, + name: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to delete a sticker set that was created by the bot. + + .. versionadded:: 20.2 + + Args: + name (:obj:`str`): Sticker set name. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"name": name} + return await self._post( + "deleteStickerSet", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_sticker_set_thumbnail( + self, + name: str, + user_id: int, + format: str, # pylint: disable=redefined-builtin + thumbnail: Optional[FileInput] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Use this method to set the thumbnail of a regular or mask sticker set. The format of the + thumbnail file must match the format of the stickers in the set. + + .. versionadded:: 20.2 + + .. versionchanged:: 21.1 + As per Bot API 7.2, the new argument :paramref:`format` will be required, and thus the + order of the arguments had to be changed. + + Args: + name (:obj:`str`): Sticker set name + user_id (:obj:`int`): User identifier of created sticker set owner. + 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. + + .. versionadded:: 21.1 + + thumbnail (:obj:`str` | :term:`file object` | :class:`~telegram.InputFile` | \ + :obj:`bytes` | :class:`pathlib.Path`, optional): A **.WEBP** or **.PNG** image + with the thumbnail, must + be up to :tg-const:`telegram.constants.StickerSetLimit.MAX_STATIC_THUMBNAIL_SIZE` + kilobytes in size and have width and height of exactly + :tg-const:`telegram.constants.StickerSetLimit.STATIC_THUMB_DIMENSIONS` px, or a + **.TGS** animation with the thumbnail up to + :tg-const:`telegram.constants.StickerSetLimit.MAX_ANIMATED_THUMBNAIL_SIZE` + kilobytes in size; see + `the docs `_ for + 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 `_ for video sticker + technical requirements. + + |fileinput| + + Animated and video sticker set thumbnails can't be uploaded via HTTP URL. If + omitted, then the thumbnail is dropped and the first sticker is used as the + thumbnail. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "name": name, + "user_id": user_id, + "thumbnail": self._parse_file_input(thumbnail) if thumbnail else None, + "format": format, + } + + return await self._post( + "setStickerSetThumbnail", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_sticker_set_title( + self, + name: str, + title: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to set the title of a created sticker set. + + .. versionadded:: 20.2 + + Args: + name (:obj:`str`): Sticker set name. + title (:obj:`str`): Sticker set title, + :tg-const:`telegram.constants.StickerLimit.MIN_NAME_AND_TITLE`- + :tg-const:`telegram.constants.StickerLimit.MAX_NAME_AND_TITLE` characters. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"name": name, "title": title} + return await self._post( + "setStickerSetTitle", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_sticker_emoji_list( + self, + sticker: str, + emoji_list: Sequence[str], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to change the list of emoji assigned to a regular or custom emoji sticker. + The sticker must belong to a sticker set created by the bot. + + .. versionadded:: 20.2 + + Args: + sticker (:obj:`str`): File identifier of the sticker. + emoji_list (Sequence[:obj:`str`]): A sequence of + :tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI`- + :tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with + the sticker. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = {"sticker": sticker, "emoji_list": emoji_list} + return await self._post( + "setStickerEmojiList", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_sticker_keywords( + self, + sticker: str, + keywords: Optional[Sequence[str]] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to change search keywords assigned to a regular or custom emoji sticker. + The sticker must belong to a sticker set created by the bot. + + .. versionadded:: 20.2 + + Args: + sticker (:obj:`str`): File identifier of the sticker. + keywords (Sequence[:obj:`str`]): A sequence of + 0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords + for the sticker with total length up to + :tg-const:`telegram.constants.StickerLimit.MAX_KEYWORD_LENGTH` characters. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = {"sticker": sticker, "keywords": keywords} + return await self._post( + "setStickerKeywords", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_sticker_mask_position( + self, + sticker: str, + mask_position: Optional[MaskPosition] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to change the mask position of a mask sticker. + The sticker must belong to a sticker set that was created by the bot. + + .. versionadded:: 20.2 + + Args: + sticker (:obj:`str`): File identifier of the sticker. + mask_position (:class:`telegram.MaskPosition`, optional): A object with the position + where the mask should be placed on faces. Omit the parameter to remove the mask + position. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = {"sticker": sticker, "mask_position": mask_position} + return await self._post( + "setStickerMaskPosition", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_custom_emoji_sticker_set_thumbnail( + self, + name: str, + custom_emoji_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to set the thumbnail of a custom emoji sticker set. + + .. versionadded:: 20.2 + + Args: + name (:obj:`str`): Sticker set name. + custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of a sticker from the + sticker set; pass an empty string to drop the thumbnail and use the first sticker + as the thumbnail. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"name": name, "custom_emoji_id": custom_emoji_id} + + return await self._post( + "setCustomEmojiStickerSetThumbnail", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_passport_data_errors( + self, + user_id: int, + errors: Sequence["PassportElementError"], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Informs a user that some of the Telegram Passport elements they provided contains errors. + The user will not be able to re-submit their Passport to you until the errors are fixed + (the contents of the field for which you returned the error must change). + + Use this if the data submitted by the user doesn't satisfy the standards your service + requires for any reason. For example, if a birthday date seems invalid, a submitted + document is blurry, a scan shows evidence of tampering, etc. Supply some details in the + error message to make sure the user knows how to correct the issues. + + Args: + user_id (:obj:`int`): User identifier + errors (Sequence[:class:`PassportElementError`]): A Sequence describing the errors. + + .. versionchanged:: 20.0 + |sequenceargs| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"user_id": user_id, "errors": errors} + return await self._post( + "setPassportDataErrors", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def send_poll( + self, + chat_id: Union[int, str], + question: str, + options: Sequence[Union[str, "InputPollOption"]], + is_anonymous: Optional[bool] = None, + type: Optional[str] = None, # pylint: disable=redefined-builtin + allows_multiple_answers: Optional[bool] = None, + correct_option_id: Optional[CorrectOptionID] = None, + is_closed: Optional[bool] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + explanation: Optional[str] = None, + explanation_parse_mode: ODVInput[str] = DEFAULT_NONE, + open_period: Optional[int] = None, + close_date: Optional[Union[int, datetime]] = None, + explanation_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + question_parse_mode: ODVInput[str] = DEFAULT_NONE, + question_entities: Optional[Sequence["MessageEntity"]] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """ + Use this method to send a native poll. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + question (:obj:`str`): Poll question, :tg-const:`telegram.Poll.MIN_QUESTION_LENGTH`- + :tg-const:`telegram.Poll.MAX_QUESTION_LENGTH` characters. + options (Sequence[:obj:`str` | :class:`telegram.InputPollOption`]): Sequence of + :tg-const:`telegram.Poll.MIN_OPTION_NUMBER`- + :tg-const:`telegram.Poll.MAX_OPTION_NUMBER` answer options. Each option may either + be a string with + :tg-const:`telegram.Poll.MIN_OPTION_LENGTH`- + :tg-const:`telegram.Poll.MAX_OPTION_LENGTH` characters or an + :class:`~telegram.InputPollOption` object. Strings are converted to + :class:`~telegram.InputPollOption` objects automatically. + + .. versionchanged:: 20.0 + |sequenceargs| + + .. versionchanged:: 21.2 + Bot API 7.3 adds support for :class:`~telegram.InputPollOption` objects. + is_anonymous (:obj:`bool`, optional): :obj:`True`, if the poll needs to be anonymous, + defaults to :obj:`True`. + type (:obj:`str`, optional): Poll type, :tg-const:`telegram.Poll.QUIZ` or + :tg-const:`telegram.Poll.REGULAR`, defaults to :tg-const:`telegram.Poll.REGULAR`. + allows_multiple_answers (:obj:`bool`, optional): :obj:`True`, if the poll allows + multiple answers, ignored for polls in quiz mode, defaults to :obj:`False`. + correct_option_id (:obj:`int`, optional): 0-based identifier of the correct answer + option, required for polls in quiz mode. + explanation (:obj:`str`, optional): Text that is shown when a user chooses an incorrect + answer or taps on the lamp icon in a quiz-style poll, + 0-:tg-const:`telegram.Poll.MAX_EXPLANATION_LENGTH` characters with at most + :tg-const:`telegram.Poll.MAX_EXPLANATION_LINE_FEEDS` line feeds after entities + parsing. + explanation_parse_mode (:obj:`str`, optional): Mode for parsing entities in the + explanation. See the constants in :class:`telegram.constants.ParseMode` for the + available modes. + explanation_entities (Sequence[:class:`telegram.MessageEntity`], optional): Sequence of + special entities that appear in message text, which can be specified instead of + :paramref:`explanation_parse_mode`. + + .. versionchanged:: 20.0 + |sequenceargs| + open_period (:obj:`int`, 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`. + 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 + :tg-const:`telegram.Poll.MAX_OPEN_PERIOD` seconds in the future. + Can't be used together with :paramref:`open_period`. + 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. + is_closed (:obj:`bool`, optional): Pass :obj:`True`, if the poll needs to be + immediately closed. This can be useful for poll preview. + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + question_parse_mode (:obj:`str`, optional): Mode for parsing entities in the question. + See the constants in :class:`telegram.constants.ParseMode` for the available modes. + Currently, only custom emoji entities are allowed. + + .. versionadded:: 21.2 + question_entities (Sequence[:class:`telegram.Message`], optional): Special entities + that appear in the poll :paramref:`question`. It can be specified instead of + :paramref:`question_parse_mode`. + + .. versionadded:: 21.2 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "question": question, + "options": [ + InputPollOption(option) if isinstance(option, str) else option + for option in options + ], + "explanation_parse_mode": explanation_parse_mode, + "is_anonymous": is_anonymous, + "type": type, + "allows_multiple_answers": allows_multiple_answers, + "correct_option_id": correct_option_id, + "is_closed": is_closed, + "explanation": explanation, + "explanation_entities": explanation_entities, + "open_period": open_period, + "close_date": close_date, + "question_parse_mode": question_parse_mode, + "question_entities": question_entities, + } + + return await self._send_message( + "sendPoll", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def stop_poll( + self, + chat_id: Union[int, str], + message_id: int, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + business_connection_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Poll: + """ + Use this method to stop a poll which was sent by the bot. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + message_id (:obj:`int`): Identifier of the original message with the poll. + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): An object for a new + message inline keyboard. + business_connection_id (:obj:`str`, optional): |business_id_str_edit| + + .. versionadded:: 21.4 + + Returns: + :class:`telegram.Poll`: On success, the stopped Poll is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_id": message_id, + "reply_markup": reply_markup, + "business_connection_id": business_connection_id, + } + + result = await self._post( + "stopPoll", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + return Poll.de_json(result, self) # type: ignore[return-value] + + async def send_dice( + self, + chat_id: Union[int, str], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + emoji: Optional[str] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """ + Use this method to send an animated emoji that will display a random value. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + disable_notification (:obj:`bool`, optional): |disable_notification| + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user + emoji (:obj:`str`, optional): Emoji on which the dice throw animation is based. + Currently, must be one of :class:`telegram.constants.DiceEmoji`. Dice can have + values + :tg-const:`telegram.Dice.MIN_VALUE`-:tg-const:`telegram.Dice.MAX_VALUE_BOWLING` + for :tg-const:`telegram.Dice.DICE`, :tg-const:`telegram.Dice.DARTS` and + :tg-const:`telegram.Dice.BOWLING`, values + :tg-const:`telegram.Dice.MIN_VALUE`-:tg-const:`telegram.Dice.MAX_VALUE_BASKETBALL` + for :tg-const:`telegram.Dice.BASKETBALL` and :tg-const:`telegram.Dice.FOOTBALL`, + and values :tg-const:`telegram.Dice.MIN_VALUE`- + :tg-const:`telegram.Dice.MAX_VALUE_SLOT_MACHINE` + for :tg-const:`telegram.Dice.SLOT_MACHINE`. Defaults to + :tg-const:`telegram.Dice.DICE`. + + .. versionchanged:: 13.4 + Added the :tg-const:`telegram.Dice.BOWLING` emoji. + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + + + Returns: + :class:`telegram.Message`: On success, the sent Message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id, "emoji": emoji} + + return await self._send_message( + "sendDice", + data, + reply_to_message_id=reply_to_message_id, + disable_notification=disable_notification, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def get_my_default_administrator_rights( + self, + for_channels: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> ChatAdministratorRights: + """Use this method to get the current default administrator rights of the bot. + + .. seealso:: :meth:`set_my_default_administrator_rights` + + .. versionadded:: 20.0 + + Args: + for_channels (:obj:`bool`, optional): Pass :obj:`True` to get default administrator + rights of the bot in channels. Otherwise, default administrator rights of the bot + for groups and supergroups will be returned. + + Returns: + :class:`telegram.ChatAdministratorRights`: On success. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = {"for_channels": for_channels} + + result = await self._post( + "getMyDefaultAdministratorRights", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + return ChatAdministratorRights.de_json(result, self) # type: ignore[return-value] + + async def set_my_default_administrator_rights( + self, + rights: Optional[ChatAdministratorRights] = None, + for_channels: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Use this method to change the default administrator rights requested by the bot when + it's added as an administrator to groups or channels. These rights will be suggested to + users, but they are free to modify the list before adding the bot. + + .. seealso:: :meth:`get_my_default_administrator_rights` + + .. versionadded:: 20.0 + + Args: + rights (:class:`telegram.ChatAdministratorRights`, optional): A + :class:`telegram.ChatAdministratorRights` object describing new default + administrator + rights. If not specified, the default administrator rights will be cleared. + for_channels (:obj:`bool`, optional): Pass :obj:`True` to change the default + administrator rights of the bot in channels. Otherwise, the default administrator + rights of the bot for groups and supergroups will be changed. + + Returns: + :obj:`bool`: Returns :obj:`True` on success. + + Raises: + :exc:`telegram.error.TelegramError` + """ + data: JSONDict = {"rights": rights, "for_channels": for_channels} + + return await self._post( + "setMyDefaultAdministratorRights", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_my_commands( + self, + scope: Optional[BotCommandScope] = None, + language_code: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple[BotCommand, ...]: + """ + Use this method to get the current list of the bot's commands for the given scope and user + language. + + .. seealso:: :meth:`set_my_commands`, :meth:`delete_my_commands` + + .. versionchanged:: 20.0 + Returns a tuple instead of a list. + + Args: + scope (:class:`telegram.BotCommandScope`, optional): An object, + describing scope of users. Defaults to :class:`telegram.BotCommandScopeDefault`. + + .. versionadded:: 13.7 + + language_code (:obj:`str`, optional): A two-letter ISO 639-1 language code or an empty + string. + + .. versionadded:: 13.7 + + Returns: + Tuple[:class:`telegram.BotCommand`]: On success, the commands set for the bot. An empty + tuple is returned if commands are not set. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"scope": scope, "language_code": language_code} + + result = await self._post( + "getMyCommands", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + return BotCommand.de_list(result, self) + + async def set_my_commands( + self, + commands: Sequence[Union[BotCommand, Tuple[str, str]]], + scope: Optional[BotCommandScope] = None, + language_code: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to change the list of the bot's commands. See the + `Telegram docs `_ for more details about + bot commands. + + .. seealso:: :meth:`get_my_commands`, :meth:`delete_my_commands` + + Args: + commands (Sequence[:class:`BotCommand` | (:obj:`str`, :obj:`str`)]): A sequence + of bot commands to be set as the list of the bot's commands. At most + :tg-const:`telegram.constants.BotCommandLimit.MAX_COMMAND_NUMBER` commands can be + specified. + + Note: + If you pass in a sequence of :obj:`tuple`, the order of elements in each + :obj:`tuple` must correspond to the order of positional arguments to create a + :class:`BotCommand` instance. + + .. versionchanged:: 20.0 + |sequenceargs| + scope (:class:`telegram.BotCommandScope`, optional): An object, + describing scope of users for which the commands are relevant. Defaults to + :class:`telegram.BotCommandScopeDefault`. + + .. versionadded:: 13.7 + + language_code (:obj:`str`, optional): A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, for whose language + there are no dedicated commands. + + .. versionadded:: 13.7 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + cmds = [c if isinstance(c, BotCommand) else BotCommand(c[0], c[1]) for c in commands] + data: JSONDict = {"commands": cmds, "scope": scope, "language_code": language_code} + + return await self._post( + "setMyCommands", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_my_commands( + self, + scope: Optional[BotCommandScope] = None, + language_code: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to delete the list of the bot's commands for the given scope and user + language. After deletion, + `higher level commands `_ + will be shown to affected users. + + .. versionadded:: 13.7 + + .. seealso:: :meth:`get_my_commands`, :meth:`set_my_commands` + + Args: + scope (:class:`telegram.BotCommandScope`, optional): An object, + describing scope of users for which the commands are relevant. Defaults to + :class:`telegram.BotCommandScopeDefault`. + language_code (:obj:`str`, optional): A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, for whose language + there are no dedicated commands. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = {"scope": scope, "language_code": language_code} + + return await self._post( + "deleteMyCommands", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def log_out( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to log out from the cloud Bot API server before launching the bot locally. + You *must* log out the bot before running it locally, otherwise there is no guarantee that + the bot will receive updates. After a successful call, you can immediately log in on a + local server, but will not be able to log in back to the cloud Bot API server for 10 + minutes. + + Returns: + :obj:`True`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + return await self._post( + "logOut", + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def close( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to close the bot instance before moving it from one local server to + another. You need to delete the webhook before calling this method to ensure that the bot + isn't launched again after server restart. The method will return error 429 in the first + 10 minutes after the bot is launched. + + Returns: + :obj:`True`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + return await self._post( + "close", + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def copy_message( + self, + chat_id: Union[int, str], + from_chat_id: Union[str, int], + message_id: int, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> MessageId: + """Use this method to copy messages of any kind. Service messages, paid media messages, + giveaway messages, giveaway winners messages, and invoice messages + can't be copied. The method is analogous to the method :meth:`forward_message`, but the + copied message doesn't have a link to the original message. + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + 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. + 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. + parse_mode (:obj:`str`, optional): Mode for parsing entities in the new caption. See + the constants in :class:`telegram.constants.ParseMode` for the available modes. + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): + |caption_entities| + + .. versionchanged:: 20.0 + |sequenceargs| + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + + .. versionadded:: 13.10 + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + + .. versionadded:: 20.0 + + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + + .. versionadded:: 20.8 + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: 21.3 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`reply_parameters` |rtm_aswr_deprecated| + + .. versionchanged:: 21.0 + |keyword_only_arg| + + Returns: + :class:`telegram.MessageId`: On success, the :class:`telegram.MessageId` of the sent + message is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + if allow_sending_without_reply is not DEFAULT_NONE and reply_parameters is not None: + raise ValueError( + "`allow_sending_without_reply` and `reply_parameters` are mutually exclusive." + ) + + if reply_to_message_id is not None and reply_parameters is not None: + raise ValueError( + "`reply_to_message_id` and `reply_parameters` are mutually exclusive." + ) + + if reply_to_message_id is not None: + reply_parameters = ReplyParameters( + message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, + ) + + data: JSONDict = { + "chat_id": chat_id, + "from_chat_id": from_chat_id, + "message_id": message_id, + "parse_mode": parse_mode, + "disable_notification": disable_notification, + "protect_content": protect_content, + "caption": caption, + "caption_entities": caption_entities, + "reply_markup": reply_markup, + "message_thread_id": message_thread_id, + "reply_parameters": reply_parameters, + "show_caption_above_media": show_caption_above_media, + } + + result = await self._post( + "copyMessage", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + return MessageId.de_json(result, self) # type: ignore[return-value] + + async def copy_messages( + self, + chat_id: Union[int, str], + from_chat_id: Union[str, int], + message_ids: Sequence[int], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + remove_caption: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple["MessageId", ...]: + """ + Use this method to copy messages of any kind. If some of the specified messages can't be + found or copied, they are skipped. Service messages, paid media messages, giveaway + messages, giveaway winners messages, and invoice messages can't be copied. A quiz poll can + be copied only if the value + of the field :attr:`telegram.Poll.correct_option_id` is known to the bot. The method is + analogous to the method :meth:`forward_messages`, but the copied messages don't have a + link to the original message. Album grouping is kept for copied messages. + + .. versionadded:: 20.8 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + 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_ids (Sequence[:obj:`int`]): A list of + :tg-const:`telegram.constants.BulkRequestLimit.MIN_LIMIT` - + :tg-const:`telegram.constants.BulkRequestLimit.MAX_LIMIT` identifiers of messages + in the chat :paramref:`from_chat_id` to copy. The identifiers must be + specified in a strictly increasing order. + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + message_thread_id (:obj:`int`, optional): |message_thread_id_arg| + remove_caption (:obj:`bool`, optional): Pass :obj:`True` to copy the messages without + their captions. + + Returns: + Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + of the sent messages is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "from_chat_id": from_chat_id, + "message_ids": message_ids, + "disable_notification": disable_notification, + "protect_content": protect_content, + "message_thread_id": message_thread_id, + "remove_caption": remove_caption, + } + + result = await self._post( + "copyMessages", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + return MessageId.de_list(result, self) + + async def set_chat_menu_button( + self, + chat_id: Optional[int] = None, + menu_button: Optional[MenuButton] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Use this method to change the bot's menu button in a private chat, or the default menu + button. + + .. seealso:: :meth:`get_chat_menu_button`, :meth:`telegram.Chat.get_menu_button` + :meth:`telegram.User.get_menu_button` + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int`, optional): Unique identifier for the target private chat. If not + specified, default bot's menu button will be changed + menu_button (:class:`telegram.MenuButton`, optional): An object for the new bot's menu + button. Defaults to :class:`telegram.MenuButtonDefault`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + data: JSONDict = {"chat_id": chat_id, "menu_button": menu_button} + + return await self._post( + "setChatMenuButton", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_chat_menu_button( + self, + chat_id: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> MenuButton: + """Use this method to get the current value of the bot's menu button in a private chat, or + the default menu button. + + .. seealso:: :meth:`set_chat_menu_button`, :meth:`telegram.Chat.set_menu_button`, + :meth:`telegram.User.set_menu_button` + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int`, optional): Unique identifier for the target private chat. If not + specified, default bot's menu button will be returned. + + Returns: + :class:`telegram.MenuButton`: On success, the current menu button is returned. + + """ + data = {"chat_id": chat_id} + + result = await self._post( + "getChatMenuButton", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + return MenuButton.de_json(result, bot=self) # type: ignore[return-value] + + 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"], + max_tip_amount: Optional[int] = None, + suggested_tip_amounts: Optional[Sequence[int]] = None, + provider_data: Optional[Union[str, object]] = None, + photo_url: Optional[str] = None, + photo_size: Optional[int] = None, + photo_width: Optional[int] = None, + photo_height: Optional[int] = None, + need_name: Optional[bool] = None, + need_phone_number: Optional[bool] = None, + need_email: Optional[bool] = None, + need_shipping_address: Optional[bool] = None, + send_phone_number_to_provider: Optional[bool] = None, + send_email_to_provider: Optional[bool] = None, + is_flexible: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> str: + """Use this method to create a link for an invoice. + + .. versionadded:: 20.0 + + Args: + title (:obj:`str`): Product name. :tg-const:`telegram.Invoice.MIN_TITLE_LENGTH`- + :tg-const:`telegram.Invoice.MAX_TITLE_LENGTH` characters. + description (:obj:`str`): Product description. + :tg-const:`telegram.Invoice.MIN_DESCRIPTION_LENGTH`- + :tg-const:`telegram.Invoice.MAX_DESCRIPTION_LENGTH` characters. + payload (:obj:`str`): Bot-defined invoice payload. + :tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`- + :tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be + displayed to the user, use it for your internal processes. + provider_token (:obj:`str`): Payments provider token, obtained via + `@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. + + currency (:obj:`str`): Three-letter ISO 4217 currency code, see `more on currencies + `_. Pass ``XTR`` for + payments in |tg_stars|. + prices (Sequence[:class:`telegram.LabeledPrice`)]: Price breakdown, a sequence + of components (e.g. product price, tax, discount, delivery cost, delivery tax, + bonus, etc.). Must contain exactly one item for payments in |tg_stars|. + + .. versionchanged:: 20.0 + |sequenceargs| + max_tip_amount (:obj:`int`, optional): The maximum accepted amount for tips in the + *smallest units* of the currency (integer, **not** float/double). For example, for + a maximum tip of ``US$ 1.45`` pass ``max_tip_amount = 145``. See the ``exp`` + parameter in `currencies.json + `_, it shows the number of + digits past the decimal point for each currency (2 for the majority of currencies). + Defaults to ``0``. Not supported for payments in |tg_stars|. + suggested_tip_amounts (Sequence[:obj:`int`], optional): An array of + suggested amounts of tips in the *smallest units* of the currency (integer, **not** + float/double). At most :tg-const:`telegram.Invoice.MAX_TIP_AMOUNTS` suggested tip + amounts can be specified. The suggested tip amounts must be positive, passed in a + strictly increased order and must not exceed :paramref:`max_tip_amount`. + + .. versionchanged:: 20.0 + |sequenceargs| + provider_data (:obj:`str` | :obj:`object`, optional): Data about the + invoice, which will be shared with the payment provider. A detailed description of + required fields should be provided by the payment provider. When an object is + passed, it will be encoded as JSON. + photo_url (:obj:`str`, optional): URL of the product photo for the invoice. Can be a + photo of the goods or a marketing image for a service. + photo_size (:obj:`int`, optional): Photo size in bytes. + photo_width (:obj:`int`, optional): Photo width. + photo_height (:obj:`int`, optional): Photo height. + need_name (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's full + name to complete the order. Ignored for payments in |tg_stars|. + need_phone_number (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's + phone number to complete the order. Ignored for payments in |tg_stars|. + need_email (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's email + address to complete the order. Ignored for payments in |tg_stars|. + need_shipping_address (:obj:`bool`, optional): Pass :obj:`True`, if you require the + user's shipping address to complete the order. Ignored for payments in + |tg_stars|. + send_phone_number_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's + phone number should be sent to provider. Ignored for payments in |tg_stars|. + send_email_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's email + address should be sent to provider. Ignored for payments in |tg_stars|. + is_flexible (:obj:`bool`, optional): Pass :obj:`True`, if the final price depends on + the shipping method. Ignored for payments in |tg_stars|. + + Returns: + :class:`str`: On success, the created invoice link is returned. + + """ + data: JSONDict = { + "title": title, + "description": description, + "payload": payload, + "provider_token": provider_token, + "currency": currency, + "prices": prices, + "max_tip_amount": max_tip_amount, + "suggested_tip_amounts": suggested_tip_amounts, + "provider_data": provider_data, + "photo_url": photo_url, + "photo_size": photo_size, + "photo_width": photo_width, + "photo_height": photo_height, + "need_name": need_name, + "need_phone_number": need_phone_number, + "need_email": need_email, + "need_shipping_address": need_shipping_address, + "is_flexible": is_flexible, + "send_phone_number_to_provider": send_phone_number_to_provider, + "send_email_to_provider": send_email_to_provider, + } + + return await self._post( + "createInvoiceLink", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_forum_topic_icon_stickers( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple[Sticker, ...]: + """Use this method to get custom emoji stickers, which can be used as a forum topic + icon by any user. Requires no parameters. + + .. versionadded:: 20.0 + + Returns: + Tuple[:class:`telegram.Sticker`] + + Raises: + :class:`telegram.error.TelegramError` + + """ + result = await self._post( + "getForumTopicIconStickers", + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + return Sticker.de_list(result, self) + + async def create_forum_topic( + self, + chat_id: Union[str, int], + name: str, + icon_color: Optional[int] = None, + icon_custom_emoji_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> ForumTopic: + """ + Use this method to create a topic in a forum supergroup chat. The bot must be + an administrator in the chat for this to work and must have + :paramref:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights. + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + name (:obj:`str`): New topic name, + :tg-const:`telegram.constants.ForumTopicLimit.MIN_NAME_LENGTH`- + :tg-const:`telegram.constants.ForumTopicLimit.MAX_NAME_LENGTH` characters. + icon_color (:obj:`int`, optional): Color of the topic icon in RGB format. Currently, + must be one of :attr:`telegram.constants.ForumIconColor.BLUE`, + :attr:`telegram.constants.ForumIconColor.YELLOW`, + :attr:`telegram.constants.ForumIconColor.PURPLE`, + :attr:`telegram.constants.ForumIconColor.GREEN`, + :attr:`telegram.constants.ForumIconColor.PINK`, or + :attr:`telegram.constants.ForumIconColor.RED`. + icon_custom_emoji_id (:obj:`str`, optional): New unique identifier of the custom emoji + shown as the topic icon. Use :meth:`~telegram.Bot.get_forum_topic_icon_stickers` + to get all allowed custom emoji identifiers. + + Returns: + :class:`telegram.ForumTopic` + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = { + "chat_id": chat_id, + "name": name, + "icon_color": icon_color, + "icon_custom_emoji_id": icon_custom_emoji_id, + } + result = await self._post( + "createForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + return ForumTopic.de_json(result, self) # type: ignore[return-value] + + async def edit_forum_topic( + self, + chat_id: Union[str, int], + message_thread_id: int, + name: Optional[str] = None, + icon_custom_emoji_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to edit name and icon of a topic in a forum supergroup chat. The bot must + be an administrator in the chat for this to work and must have the + :paramref:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights, + unless it is the creator of the topic. + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + message_thread_id (:obj:`int`): |message_thread_id| + name (:obj:`str`, optional): New topic name, + :tg-const:`telegram.constants.ForumTopicLimit.MIN_NAME_LENGTH`- + :tg-const:`telegram.constants.ForumTopicLimit.MAX_NAME_LENGTH` characters. If + not specified or empty, the current name of the topic will be kept. + icon_custom_emoji_id (:obj:`str`, optional): New unique identifier of the custom emoji + shown as the topic icon. Use :meth:`~telegram.Bot.get_forum_topic_icon_stickers` + to get all allowed custom emoji identifiers.Pass an empty string to remove the + icon. If not specified, the current icon will be kept. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_thread_id": message_thread_id, + "name": name, + "icon_custom_emoji_id": icon_custom_emoji_id, + } + return await self._post( + "editForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def close_forum_topic( + self, + chat_id: Union[str, int], + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to close an open topic in a forum supergroup chat. The bot must + be an administrator in the chat for this to work and must have + :paramref:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights, + unless it is the creator of the topic. + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + message_thread_id (:obj:`int`): |message_thread_id| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_thread_id": message_thread_id, + } + return await self._post( + "closeForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def reopen_forum_topic( + self, + chat_id: Union[str, int], + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to reopen a closed topic in a forum supergroup chat. The bot must + be an administrator in the chat for this to work and must have + :paramref:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights, + unless it is the creator of the topic. + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + message_thread_id (:obj:`int`): |message_thread_id| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_thread_id": message_thread_id, + } + return await self._post( + "reopenForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_forum_topic( + self, + chat_id: Union[str, int], + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to delete a forum topic along with all its messages in a forum supergroup + chat. The bot must be an administrator in the chat for this to work and must have + :paramref:`~telegram.ChatAdministratorRights.can_delete_messages` administrator rights. + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + message_thread_id (:obj:`int`): |message_thread_id| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_thread_id": message_thread_id, + } + return await self._post( + "deleteForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unpin_all_forum_topic_messages( + self, + chat_id: Union[str, int], + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to clear the list of pinned messages in a forum topic. The bot must + be an administrator in the chat for this to work and must have + :paramref:`~telegram.ChatAdministratorRights.can_pin_messages` administrator rights + in the supergroup. + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + message_thread_id (:obj:`int`): |message_thread_id| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_thread_id": message_thread_id, + } + return await self._post( + "unpinAllForumTopicMessages", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unpin_all_general_forum_topic_messages( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to clear the list of pinned messages in a General forum topic. The bot must + be an administrator in the chat for this to work and must have + :paramref:`~telegram.ChatAdministratorRights.can_pin_messages` administrator rights in the + supergroup. + + .. versionadded:: 20.5 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = {"chat_id": chat_id} + + return await self._post( + "unpinAllGeneralForumTopicMessages", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def edit_general_forum_topic( + self, + chat_id: Union[str, int], + name: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to edit the name of the 'General' topic in a forum supergroup chat. The bot + must be an administrator in the chat for this to work and must have the + :attr:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights. + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + name (:obj:`str`): New topic name, + :tg-const:`telegram.constants.ForumTopicLimit.MIN_NAME_LENGTH`- + :tg-const:`telegram.constants.ForumTopicLimit.MAX_NAME_LENGTH` characters. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id, "name": name} + + return await self._post( + "editGeneralForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def close_general_forum_topic( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to close an open 'General' topic in a forum supergroup chat. The bot must + be an administrator in the chat for this to work and must have + :attr:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights. + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id} + + return await self._post( + "closeGeneralForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def reopen_general_forum_topic( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to reopen a closed 'General' topic in a forum supergroup chat. The bot must + be an administrator in the chat for this to work and must have + :attr:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights. + The topic will be automatically unhidden if it was hidden. + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id} + + return await self._post( + "reopenGeneralForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def hide_general_forum_topic( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to hide the 'General' topic in a forum supergroup chat. The bot must + be an administrator in the chat for this to work and must have + :attr:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights. + The topic will be automatically closed if it was open. + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id} + + return await self._post( + "hideGeneralForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unhide_general_forum_topic( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to unhide the 'General' topic in a forum supergroup chat. The bot must + be an administrator in the chat for this to work and must have + :attr:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights. + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"chat_id": chat_id} + + return await self._post( + "unhideGeneralForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_my_description( + self, + description: Optional[str] = None, + language_code: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to change the bot's description, which is shown in the chat with the bot + if the chat is empty. + + .. versionadded:: 20.2 + + Args: + description (:obj:`str`, optional): New bot description; + 0-:tg-const:`telegram.constants.BotDescriptionLimit.MAX_DESCRIPTION_LENGTH` + characters. Pass an empty string to remove the dedicated description for the given + language. + language_code (:obj:`str`, optional): A two-letter ISO 639-1 language code. If empty, + the description will be applied to all users for whose language there is no + dedicated description. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"description": description, "language_code": language_code} + + return await self._post( + "setMyDescription", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_my_short_description( + self, + short_description: Optional[str] = None, + language_code: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to change the bot's short description, which is shown on the bot's profile + page and is sent together with the link when users share the bot. + + .. versionadded:: 20.2 + + Args: + short_description (:obj:`str`, optional): New short description for the bot; + 0-:tg-const:`telegram.constants.BotDescriptionLimit.MAX_SHORT_DESCRIPTION_LENGTH` + characters. Pass an empty string to remove the dedicated description for the given + language. + language_code (:obj:`str`, optional): A two-letter ISO 639-1 language code. If empty, + the description will be applied to all users for whose language there is no + dedicated description. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"short_description": short_description, "language_code": language_code} + + return await self._post( + "setMyShortDescription", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_my_description( + self, + language_code: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> BotDescription: + """ + Use this method to get the current bot description for the given user language. + + Args: + language_code (:obj:`str`, optional): A two-letter ISO 639-1 language code or an empty + string. + + Returns: + :class:`telegram.BotDescription`: On success, the bot description is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data = {"language_code": language_code} + return BotDescription.de_json( # type: ignore[return-value] + await self._post( + "getMyDescription", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ), + bot=self, + ) + + async def get_my_short_description( + self, + language_code: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> BotShortDescription: + """ + Use this method to get the current bot short description for the given user language. + + Args: + language_code (:obj:`str`, optional): A two-letter ISO 639-1 language code or an empty + string. + + Returns: + :class:`telegram.BotShortDescription`: On success, the bot short description is + returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data = {"language_code": language_code} + return BotShortDescription.de_json( # type: ignore[return-value] + await self._post( + "getMyShortDescription", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ), + bot=self, + ) + + async def set_my_name( + self, + name: Optional[str] = None, + language_code: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to change the bot's name. + + .. versionadded:: 20.3 + + Args: + name (:obj:`str`, optional): New bot name; + 0-:tg-const:`telegram.constants.BotNameLimit.MAX_NAME_LENGTH` + characters. Pass an empty string to remove the dedicated name for the given + language. + + Caution: + If :paramref:`language_code` is not specified, a :paramref:`name` *must* + be specified. + language_code (:obj:`str`, optional): A two-letter ISO 639-1 language code. If empty, + the name will be applied to all users for whose language there is no + dedicated name. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = {"name": name, "language_code": language_code} + + return await self._post( + "setMyName", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_my_name( + self, + language_code: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> BotName: + """ + Use this method to get the current bot name for the given user language. + + Args: + language_code (:obj:`str`, optional): A two-letter ISO 639-1 language code or an empty + string. + + Returns: + :class:`telegram.BotName`: On success, the bot name is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data = {"language_code": language_code} + return BotName.de_json( # type: ignore[return-value] + await self._post( + "getMyName", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ), + bot=self, + ) + + async def get_user_chat_boosts( + self, + chat_id: Union[str, int], + user_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> UserChatBoosts: + """ + Use this method to get the list of boosts added to a chat by a user. Requires + administrator rights in the chat. + + .. versionadded:: 20.8 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + user_id (:obj:`int`): Unique identifier of the target user. + + Returns: + :class:`telegram.UserChatBoosts`: On success, the object containing the list of boosts + is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = {"chat_id": chat_id, "user_id": user_id} + return UserChatBoosts.de_json( # type: ignore[return-value] + await self._post( + "getUserChatBoosts", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ), + bot=self, + ) + + async def set_message_reaction( + self, + chat_id: Union[str, int], + message_id: int, + reaction: Optional[Union[Sequence[Union[ReactionType, str]], ReactionType, str]] = None, + is_big: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """ + Use this method to change the chosen reactions on a message. Service messages can't be + reacted to. Automatically forwarded messages from a channel to its discussion group have + the same available reactions as messages in the channel. Bots can't use paid reactions. + + .. versionadded:: 20.8 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + message_id (:obj:`int`): Identifier of the target message. If the message belongs to a + media group, the reaction is set to the first non-deleted message in the group + instead. + reaction (Sequence[:class:`telegram.ReactionType` | :obj:`str`] | \ + :class:`telegram.ReactionType` | :obj:`str`, optional): A list of reaction + types to set on the message. Currently, as non-premium users, bots can set up to + one reaction per message. A custom emoji reaction can be used if it is either + already present on the message or explicitly allowed by chat administrators. Paid + reactions can't be used by bots. + + Tip: + Passed :obj:`str` values will be converted to either + :class:`telegram.ReactionTypeEmoji` or + :class:`telegram.ReactionTypeCustomEmoji` + depending on whether they are listed in + :class:`~telegram.constants.ReactionEmoji`. + + is_big (:obj:`bool`, optional): Pass :obj:`True` to set the reaction with a big + animation. + + Returns: + :obj:`bool` On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + allowed_reactions: Set[str] = set(ReactionEmoji) + parsed_reaction = ( + [ + ( + entry + if isinstance(entry, ReactionType) + else ( + ReactionTypeEmoji(emoji=entry) + if entry in allowed_reactions + else ReactionTypeCustomEmoji(custom_emoji_id=entry) + ) + ) + for entry in ( + [reaction] if isinstance(reaction, (ReactionType, str)) else reaction + ) + ] + if reaction is not None + else None + ) + + data: JSONDict = { + "chat_id": chat_id, + "message_id": message_id, + "reaction": parsed_reaction, + "is_big": is_big, + } + + return await self._post( + "setMessageReaction", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_business_connection( + self, + business_connection_id: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> BusinessConnection: + """ + Use this method to get information about the connection of the bot with a business account. + + .. versionadded:: 21.1 + + Args: + business_connection_id (:obj:`str`): Unique identifier of the business connection. + + Returns: + :class:`telegram.BusinessConnection`: On success, the object containing the business + connection information is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = {"business_connection_id": business_connection_id} + return BusinessConnection.de_json( # type: ignore[return-value] + await self._post( + "getBusinessConnection", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ), + bot=self, + ) + + async def replace_sticker_in_set( + self, + user_id: int, + name: str, + old_sticker: str, + sticker: "InputSticker", + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Use this method to replace an existing sticker in a sticker set with a new one. + The method is equivalent to calling :meth:`delete_sticker_from_set`, + then :meth:`add_sticker_to_set`, then :meth:`set_sticker_position_in_set`. + + .. versionadded:: 21.1 + + Args: + user_id (:obj:`int`): User identifier of the sticker set owner. + name (:obj:`str`): Sticker set name. + old_sticker (:obj:`str`): File identifier of the replaced sticker. + sticker (:class:`telegram.InputSticker`): An object with information about the added + sticker. If exactly the same sticker had already been added to the set, then the + set remains unchanged. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = { + "user_id": user_id, + "name": name, + "old_sticker": old_sticker, + "sticker": sticker, + } + + return await self._post( + "replaceStickerInSet", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def refund_star_payment( + self, + user_id: int, + telegram_payment_charge_id: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Refunds a successful payment in |tg_stars|. + + .. versionadded:: 21.3 + + Args: + user_id (:obj:`int`): User identifier of the user whose payment will be refunded. + telegram_payment_charge_id (:obj:`str`): Telegram payment identifier. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "user_id": user_id, + "telegram_payment_charge_id": telegram_payment_charge_id, + } + + return await self._post( + "refundStarPayment", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_star_transactions( + self, + offset: Optional[int] = None, + limit: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> StarTransactions: + """Returns the bot's Telegram Star transactions in chronological order. + + .. versionadded:: 21.4 + + Args: + offset (:obj:`int`, optional): Number of transactions to skip in the response. + limit (:obj:`int`, optional): The maximum number of transactions to be retrieved. + Values between :tg-const:`telegram.constants.StarTransactionsLimit.MIN_LIMIT`- + :tg-const:`telegram.constants.StarTransactionsLimit.MAX_LIMIT` are accepted. + Defaults to :tg-const:`telegram.constants.StarTransactionsLimit.MAX_LIMIT`. + + Returns: + :class:`telegram.StarTransactions`: On success. + + Raises: + :class:`telegram.error.TelegramError` + """ + + data: JSONDict = {"offset": offset, "limit": limit} + + return StarTransactions.de_json( # type: ignore[return-value] + await self._post( + "getStarTransactions", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ), + bot=self, + ) + + async def send_paid_media( + self, + chat_id: Union[str, int], + star_count: int, + media: Sequence["InputPaidMedia"], + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + show_caption_above_media: Optional[bool] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + reply_markup: Optional[ReplyMarkup] = None, + business_connection_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Message: + """Use this method to send paid media. + + .. versionadded:: 21.4 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| If the chat is a channel, all + Telegram Star proceeds from this media will be credited to the chat's balance. + Otherwise, they will be credited to the bot's balance. + star_count (:obj:`int`): The number of Telegram Stars that must be paid to buy access + to the media. + media (Sequence[:class:`telegram.InputPaidMedia`]): A list describing the media to be + sent; up to :tg-const:`telegram.constants.MediaGroupLimit.MAX_MEDIA_LENGTH` items. + caption (:obj:`str`, optional): Caption of the media to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): + |caption_entities| + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + disable_notification (:obj:`bool`, optional): |disable_notification| + protect_content (:obj:`bool`, optional): |protect_content| + reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| + reply_markup (:class:`InlineKeyboardMarkup` | :class:`ReplyKeyboardMarkup` | \ + :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): + Additional interface options. An object for an inline keyboard, custom reply + keyboard, instructions to remove reply keyboard or to force a reply from the user. + business_connection_id (:obj:`str`, optional): |business_id_str| + + .. versionadded:: 21.5 + + Keyword Args: + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + reply_to_message_id (:obj:`int`, optional): |reply_to_msg_id| + Mutually exclusive with :paramref:`reply_parameters`, which this is a convenience + parameter for + + Returns: + :class:`telegram.Message`: On success, the sent message is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + + data: JSONDict = { + "chat_id": chat_id, + "star_count": star_count, + "media": media, + "show_caption_above_media": show_caption_above_media, + } + + return await self._send_message( + "sendPaidMedia", + data, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + disable_notification=disable_notification, + protect_content=protect_content, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + reply_to_message_id=reply_to_message_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + ) + + async def create_chat_subscription_invite_link( + self, + chat_id: Union[str, int], + subscription_period: int, + subscription_price: int, + name: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> ChatInviteLink: + """ + Use this method to create a `subscription invite link `_ for a channel chat. + The bot must have the :attr:`~telegram.ChatPermissions.can_invite_users` administrator + right. The link can be edited using the :meth:`edit_chat_subscription_invite_link` or + revoked using the :meth:`revoke_chat_invite_link`. + + .. versionadded:: 21.5 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + subscription_period (:obj:`int`): The number of seconds the subscription will be + active for before the next payment. Currently, it must always be + :tg-const:`telegram.constants.ChatSubscriptionLimit.SUBSCRIPTION_PERIOD` (30 days). + subscription_price (:obj:`int`): The number of Telegram Stars a user must pay initially + and after each subsequent subscription period to be a member of the chat; + :tg-const:`telegram.constants.ChatSubscriptionLimit.MIN_PRICE`- + :tg-const:`telegram.constants.ChatSubscriptionLimit.MAX_PRICE`. + name (:obj:`str`, optional): Invite link name; + 0-:tg-const:`telegram.constants.ChatInviteLinkLimit.NAME_LENGTH` characters. + + Returns: + :class:`telegram.ChatInviteLink` + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "subscription_period": subscription_period, + "subscription_price": subscription_price, + "name": name, + } + + result = await self._post( + "createChatSubscriptionInviteLink", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + return ChatInviteLink.de_json(result, self) # type: ignore[return-value] + + async def edit_chat_subscription_invite_link( + self, + chat_id: Union[str, int], + invite_link: Union[str, "ChatInviteLink"], + name: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> ChatInviteLink: + """ + Use this method to edit a subscription invite link created by the bot. The bot must have + :attr:`telegram.ChatPermissions.can_invite_users` administrator right. + + .. versionadded:: 21.5 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| + invite_link (:obj:`str` | :obj:`telegram.ChatInviteLink`): The invite link to edit. + name (:obj:`str`, optional): Invite link name; + 0-:tg-const:`telegram.constants.ChatInviteLinkLimit.NAME_LENGTH` characters. + + Tip: + Omitting this argument removes the name of the invite link. + + Returns: + :class:`telegram.ChatInviteLink` + + Raises: + :class:`telegram.error.TelegramError` + + """ + link = invite_link.invite_link if isinstance(invite_link, ChatInviteLink) else invite_link + data: JSONDict = { + "chat_id": chat_id, + "invite_link": link, + "name": name, + } + + result = await self._post( + "editChatSubscriptionInviteLink", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + return ChatInviteLink.de_json(result, self) # type: ignore[return-value] + + def to_dict(self, recursive: bool = True) -> JSONDict: # noqa: ARG002 + """See :meth:`telegram.TelegramObject.to_dict`.""" + data: JSONDict = {"id": self.id, "username": self.username, "first_name": self.first_name} + + if self.last_name: + data["last_name"] = self.last_name + + return data + + # camelCase aliases + getMe = get_me + """Alias for :meth:`get_me`""" + sendMessage = send_message + """Alias for :meth:`send_message`""" + deleteMessage = delete_message + """Alias for :meth:`delete_message`""" + deleteMessages = delete_messages + """Alias for :meth:`delete_messages`""" + forwardMessage = forward_message + """Alias for :meth:`forward_message`""" + forwardMessages = forward_messages + """Alias for :meth:`forward_messages`""" + sendPhoto = send_photo + """Alias for :meth:`send_photo`""" + sendAudio = send_audio + """Alias for :meth:`send_audio`""" + sendDocument = send_document + """Alias for :meth:`send_document`""" + sendSticker = send_sticker + """Alias for :meth:`send_sticker`""" + sendVideo = send_video + """Alias for :meth:`send_video`""" + sendAnimation = send_animation + """Alias for :meth:`send_animation`""" + sendVoice = send_voice + """Alias for :meth:`send_voice`""" + sendVideoNote = send_video_note + """Alias for :meth:`send_video_note`""" + sendMediaGroup = send_media_group + """Alias for :meth:`send_media_group`""" + sendLocation = send_location + """Alias for :meth:`send_location`""" + editMessageLiveLocation = edit_message_live_location + """Alias for :meth:`edit_message_live_location`""" + stopMessageLiveLocation = stop_message_live_location + """Alias for :meth:`stop_message_live_location`""" + sendVenue = send_venue + """Alias for :meth:`send_venue`""" + sendContact = send_contact + """Alias for :meth:`send_contact`""" + sendGame = send_game + """Alias for :meth:`send_game`""" + sendChatAction = send_chat_action + """Alias for :meth:`send_chat_action`""" + answerInlineQuery = answer_inline_query + """Alias for :meth:`answer_inline_query`""" + getUserProfilePhotos = get_user_profile_photos + """Alias for :meth:`get_user_profile_photos`""" + getFile = get_file + """Alias for :meth:`get_file`""" + banChatMember = ban_chat_member + """Alias for :meth:`ban_chat_member`""" + banChatSenderChat = ban_chat_sender_chat + """Alias for :meth:`ban_chat_sender_chat`""" + unbanChatMember = unban_chat_member + """Alias for :meth:`unban_chat_member`""" + unbanChatSenderChat = unban_chat_sender_chat + """Alias for :meth:`unban_chat_sender_chat`""" + answerCallbackQuery = answer_callback_query + """Alias for :meth:`answer_callback_query`""" + editMessageText = edit_message_text + """Alias for :meth:`edit_message_text`""" + editMessageCaption = edit_message_caption + """Alias for :meth:`edit_message_caption`""" + editMessageMedia = edit_message_media + """Alias for :meth:`edit_message_media`""" + editMessageReplyMarkup = edit_message_reply_markup + """Alias for :meth:`edit_message_reply_markup`""" + getUpdates = get_updates + """Alias for :meth:`get_updates`""" + setWebhook = set_webhook + """Alias for :meth:`set_webhook`""" + deleteWebhook = delete_webhook + """Alias for :meth:`delete_webhook`""" + leaveChat = leave_chat + """Alias for :meth:`leave_chat`""" + getChat = get_chat + """Alias for :meth:`get_chat`""" + getChatAdministrators = get_chat_administrators + """Alias for :meth:`get_chat_administrators`""" + getChatMember = get_chat_member + """Alias for :meth:`get_chat_member`""" + setChatStickerSet = set_chat_sticker_set + """Alias for :meth:`set_chat_sticker_set`""" + deleteChatStickerSet = delete_chat_sticker_set + """Alias for :meth:`delete_chat_sticker_set`""" + getChatMemberCount = get_chat_member_count + """Alias for :meth:`get_chat_member_count`""" + getWebhookInfo = get_webhook_info + """Alias for :meth:`get_webhook_info`""" + setGameScore = set_game_score + """Alias for :meth:`set_game_score`""" + getGameHighScores = get_game_high_scores + """Alias for :meth:`get_game_high_scores`""" + sendInvoice = send_invoice + """Alias for :meth:`send_invoice`""" + answerShippingQuery = answer_shipping_query + """Alias for :meth:`answer_shipping_query`""" + answerPreCheckoutQuery = answer_pre_checkout_query + """Alias for :meth:`answer_pre_checkout_query`""" + answerWebAppQuery = answer_web_app_query + """Alias for :meth:`answer_web_app_query`""" + restrictChatMember = restrict_chat_member + """Alias for :meth:`restrict_chat_member`""" + promoteChatMember = promote_chat_member + """Alias for :meth:`promote_chat_member`""" + setChatPermissions = set_chat_permissions + """Alias for :meth:`set_chat_permissions`""" + setChatAdministratorCustomTitle = set_chat_administrator_custom_title + """Alias for :meth:`set_chat_administrator_custom_title`""" + exportChatInviteLink = export_chat_invite_link + """Alias for :meth:`export_chat_invite_link`""" + createChatInviteLink = create_chat_invite_link + """Alias for :meth:`create_chat_invite_link`""" + editChatInviteLink = edit_chat_invite_link + """Alias for :meth:`edit_chat_invite_link`""" + revokeChatInviteLink = revoke_chat_invite_link + """Alias for :meth:`revoke_chat_invite_link`""" + approveChatJoinRequest = approve_chat_join_request + """Alias for :meth:`approve_chat_join_request`""" + declineChatJoinRequest = decline_chat_join_request + """Alias for :meth:`decline_chat_join_request`""" + setChatPhoto = set_chat_photo + """Alias for :meth:`set_chat_photo`""" + deleteChatPhoto = delete_chat_photo + """Alias for :meth:`delete_chat_photo`""" + setChatTitle = set_chat_title + """Alias for :meth:`set_chat_title`""" + setChatDescription = set_chat_description + """Alias for :meth:`set_chat_description`""" + pinChatMessage = pin_chat_message + """Alias for :meth:`pin_chat_message`""" + unpinChatMessage = unpin_chat_message + """Alias for :meth:`unpin_chat_message`""" + unpinAllChatMessages = unpin_all_chat_messages + """Alias for :meth:`unpin_all_chat_messages`""" + getCustomEmojiStickers = get_custom_emoji_stickers + """Alias for :meth:`get_custom_emoji_stickers`""" + getStickerSet = get_sticker_set + """Alias for :meth:`get_sticker_set`""" + uploadStickerFile = upload_sticker_file + """Alias for :meth:`upload_sticker_file`""" + createNewStickerSet = create_new_sticker_set + """Alias for :meth:`create_new_sticker_set`""" + addStickerToSet = add_sticker_to_set + """Alias for :meth:`add_sticker_to_set`""" + setStickerPositionInSet = set_sticker_position_in_set + """Alias for :meth:`set_sticker_position_in_set`""" + deleteStickerFromSet = delete_sticker_from_set + """Alias for :meth:`delete_sticker_from_set`""" + setStickerSetThumbnail = set_sticker_set_thumbnail + """Alias for :meth:`set_sticker_set_thumbnail`""" + setPassportDataErrors = set_passport_data_errors + """Alias for :meth:`set_passport_data_errors`""" + sendPoll = send_poll + """Alias for :meth:`send_poll`""" + stopPoll = stop_poll + """Alias for :meth:`stop_poll`""" + sendDice = send_dice + """Alias for :meth:`send_dice`""" + getMyCommands = get_my_commands + """Alias for :meth:`get_my_commands`""" + setMyCommands = set_my_commands + """Alias for :meth:`set_my_commands`""" + deleteMyCommands = delete_my_commands + """Alias for :meth:`delete_my_commands`""" + logOut = log_out + """Alias for :meth:`log_out`""" + copyMessage = copy_message + """Alias for :meth:`copy_message`""" + copyMessages = copy_messages + """Alias for :meth:`copy_messages`""" + getChatMenuButton = get_chat_menu_button + """Alias for :meth:`get_chat_menu_button`""" + setChatMenuButton = set_chat_menu_button + """Alias for :meth:`set_chat_menu_button`""" + getMyDefaultAdministratorRights = get_my_default_administrator_rights + """Alias for :meth:`get_my_default_administrator_rights`""" + setMyDefaultAdministratorRights = set_my_default_administrator_rights + """Alias for :meth:`set_my_default_administrator_rights`""" + createInvoiceLink = create_invoice_link + """Alias for :meth:`create_invoice_link`""" + getForumTopicIconStickers = get_forum_topic_icon_stickers + """Alias for :meth:`get_forum_topic_icon_stickers`""" + createForumTopic = create_forum_topic + """Alias for :meth:`create_forum_topic`""" + editForumTopic = edit_forum_topic + """Alias for :meth:`edit_forum_topic`""" + closeForumTopic = close_forum_topic + """Alias for :meth:`close_forum_topic`""" + reopenForumTopic = reopen_forum_topic + """Alias for :meth:`reopen_forum_topic`""" + deleteForumTopic = delete_forum_topic + """Alias for :meth:`delete_forum_topic`""" + unpinAllForumTopicMessages = unpin_all_forum_topic_messages + """Alias for :meth:`unpin_all_forum_topic_messages`""" + editGeneralForumTopic = edit_general_forum_topic + """Alias for :meth:`edit_general_forum_topic`""" + closeGeneralForumTopic = close_general_forum_topic + """Alias for :meth:`close_general_forum_topic`""" + reopenGeneralForumTopic = reopen_general_forum_topic + """Alias for :meth:`reopen_general_forum_topic`""" + hideGeneralForumTopic = hide_general_forum_topic + """Alias for :meth:`hide_general_forum_topic`""" + unhideGeneralForumTopic = unhide_general_forum_topic + """Alias for :meth:`unhide_general_forum_topic`""" + setMyDescription = set_my_description + """Alias for :meth:`set_my_description`""" + setMyShortDescription = set_my_short_description + """Alias for :meth:`set_my_short_description`""" + getMyDescription = get_my_description + """Alias for :meth:`get_my_description`""" + getMyShortDescription = get_my_short_description + """Alias for :meth:`get_my_short_description`""" + setCustomEmojiStickerSetThumbnail = set_custom_emoji_sticker_set_thumbnail + """Alias for :meth:`set_custom_emoji_sticker_set_thumbnail`""" + setStickerSetTitle = set_sticker_set_title + """Alias for :meth:`set_sticker_set_title`""" + deleteStickerSet = delete_sticker_set + """Alias for :meth:`delete_sticker_set`""" + setStickerEmojiList = set_sticker_emoji_list + """Alias for :meth:`set_sticker_emoji_list`""" + setStickerKeywords = set_sticker_keywords + """Alias for :meth:`set_sticker_keywords`""" + setStickerMaskPosition = set_sticker_mask_position + """Alias for :meth:`set_sticker_mask_position`""" + setMyName = set_my_name + """Alias for :meth:`set_my_name`""" + getMyName = get_my_name + """Alias for :meth:`get_my_name`""" + unpinAllGeneralForumTopicMessages = unpin_all_general_forum_topic_messages + """Alias for :meth:`unpin_all_general_forum_topic_messages`""" + getUserChatBoosts = get_user_chat_boosts + """Alias for :meth:`get_user_chat_boosts`""" + setMessageReaction = set_message_reaction + """Alias for :meth:`set_message_reaction`""" + getBusinessConnection = get_business_connection + """Alias for :meth:`get_business_connection`""" + replaceStickerInSet = replace_sticker_in_set + """Alias for :meth:`replace_sticker_in_set`""" + refundStarPayment = refund_star_payment + """Alias for :meth:`refund_star_payment`""" + getStarTransactions = get_star_transactions + """Alias for :meth:`get_star_transactions`""" + sendPaidMedia = send_paid_media + """Alias for :meth:`send_paid_media`""" + createChatSubscriptionInviteLink = create_chat_subscription_invite_link + """Alias for :meth:`create_chat_subscription_invite_link`""" + editChatSubscriptionInviteLink = edit_chat_subscription_invite_link + """Alias for :meth:`edit_chat_subscription_invite_link`""" diff --git a/_botcommand.py b/_botcommand.py new file mode 100644 index 0000000000000000000000000000000000000000..972db7c240202040dae9c24daad6fe915b96d884 --- /dev/null +++ b/_botcommand.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram Bot Command.""" + +from typing import Final, Optional + +from telegram import constants +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class BotCommand(TelegramObject): + """ + This object represents a bot command. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`command` and :attr:`description` are equal. + + Args: + command (:obj:`str`): Text of the command; :tg-const:`telegram.BotCommand.MIN_COMMAND`- + :tg-const:`telegram.BotCommand.MAX_COMMAND` characters. Can contain only lowercase + English letters, digits and underscores. + description (:obj:`str`): Description of the command; + :tg-const:`telegram.BotCommand.MIN_DESCRIPTION`- + :tg-const:`telegram.BotCommand.MAX_DESCRIPTION` characters. + + Attributes: + command (:obj:`str`): Text of the command; :tg-const:`telegram.BotCommand.MIN_COMMAND`- + :tg-const:`telegram.BotCommand.MAX_COMMAND` characters. Can contain only lowercase + English letters, digits and underscores. + description (:obj:`str`): Description of the command; + :tg-const:`telegram.BotCommand.MIN_DESCRIPTION`- + :tg-const:`telegram.BotCommand.MAX_DESCRIPTION` characters. + + """ + + __slots__ = ("command", "description") + + def __init__(self, command: str, description: str, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + self.command: str = command + self.description: str = description + + self._id_attrs = (self.command, self.description) + + self._freeze() + + MIN_COMMAND: Final[int] = constants.BotCommandLimit.MIN_COMMAND + """:const:`telegram.constants.BotCommandLimit.MIN_COMMAND` + + .. versionadded:: 20.0 + """ + MAX_COMMAND: Final[int] = constants.BotCommandLimit.MAX_COMMAND + """:const:`telegram.constants.BotCommandLimit.MAX_COMMAND` + + .. versionadded:: 20.0 + """ + MIN_DESCRIPTION: Final[int] = constants.BotCommandLimit.MIN_DESCRIPTION + """:const:`telegram.constants.BotCommandLimit.MIN_DESCRIPTION` + + .. versionadded:: 20.0 + """ + MAX_DESCRIPTION: Final[int] = constants.BotCommandLimit.MAX_DESCRIPTION + """:const:`telegram.constants.BotCommandLimit.MAX_DESCRIPTION` + + .. versionadded:: 20.0 + """ diff --git a/_botcommandscope.py b/_botcommandscope.py new file mode 100644 index 0000000000000000000000000000000000000000..73cafd175998c144fcf3a22b2f3954ba24975cd7 --- /dev/null +++ b/_botcommandscope.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=redefined-builtin +"""This module contains objects representing Telegram bot command scopes.""" +from typing import TYPE_CHECKING, Dict, Final, Optional, Type, Union + +from telegram import constants +from telegram._telegramobject import TelegramObject +from telegram._utils import enum +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class BotCommandScope(TelegramObject): + """Base class for objects that represent the scope to which bot commands are applied. + Currently, the following 7 scopes are supported: + + * :class:`telegram.BotCommandScopeDefault` + * :class:`telegram.BotCommandScopeAllPrivateChats` + * :class:`telegram.BotCommandScopeAllGroupChats` + * :class:`telegram.BotCommandScopeAllChatAdministrators` + * :class:`telegram.BotCommandScopeChat` + * :class:`telegram.BotCommandScopeChatAdministrators` + * :class:`telegram.BotCommandScopeChatMember` + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type` is equal. For subclasses with additional attributes, + the notion of equality is overridden. + + Note: + Please see the `official docs`_ on how Telegram determines which commands to display. + + .. _`official docs`: https://core.telegram.org/bots/api#determining-list-of-commands + + .. versionadded:: 13.7 + + Args: + type (:obj:`str`): Scope type. + + Attributes: + type (:obj:`str`): Scope type. + """ + + __slots__ = ("type",) + + DEFAULT: Final[str] = constants.BotCommandScopeType.DEFAULT + """:const:`telegram.constants.BotCommandScopeType.DEFAULT`""" + ALL_PRIVATE_CHATS: Final[str] = constants.BotCommandScopeType.ALL_PRIVATE_CHATS + """:const:`telegram.constants.BotCommandScopeType.ALL_PRIVATE_CHATS`""" + ALL_GROUP_CHATS: Final[str] = constants.BotCommandScopeType.ALL_GROUP_CHATS + """:const:`telegram.constants.BotCommandScopeType.ALL_GROUP_CHATS`""" + ALL_CHAT_ADMINISTRATORS: Final[str] = constants.BotCommandScopeType.ALL_CHAT_ADMINISTRATORS + """:const:`telegram.constants.BotCommandScopeType.ALL_CHAT_ADMINISTRATORS`""" + CHAT: Final[str] = constants.BotCommandScopeType.CHAT + """:const:`telegram.constants.BotCommandScopeType.CHAT`""" + CHAT_ADMINISTRATORS: Final[str] = constants.BotCommandScopeType.CHAT_ADMINISTRATORS + """:const:`telegram.constants.BotCommandScopeType.CHAT_ADMINISTRATORS`""" + CHAT_MEMBER: Final[str] = constants.BotCommandScopeType.CHAT_MEMBER + """:const:`telegram.constants.BotCommandScopeType.CHAT_MEMBER`""" + + def __init__(self, type: str, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + self.type: str = enum.get_member(constants.BotCommandScopeType, type, type) + self._id_attrs = (self.type,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["BotCommandScope"]: + """Converts JSON data to the appropriate :class:`BotCommandScope` object, i.e. takes + care of selecting the correct subclass. + + Args: + data (Dict[:obj:`str`, ...]): The JSON data. + bot (:class:`telegram.Bot`, optional): The bot associated with this object. Defaults to + :obj:`None`, in which case shortcut methods will not be available. + + .. versionchanged:: 21.4 + :paramref:`bot` is now optional and defaults to :obj:`None` + + Returns: + The Telegram object. + + """ + data = cls._parse_data(data) + + if not data: + return None + + _class_mapping: Dict[str, Type[BotCommandScope]] = { + cls.DEFAULT: BotCommandScopeDefault, + cls.ALL_PRIVATE_CHATS: BotCommandScopeAllPrivateChats, + cls.ALL_GROUP_CHATS: BotCommandScopeAllGroupChats, + cls.ALL_CHAT_ADMINISTRATORS: BotCommandScopeAllChatAdministrators, + cls.CHAT: BotCommandScopeChat, + cls.CHAT_ADMINISTRATORS: BotCommandScopeChatAdministrators, + cls.CHAT_MEMBER: BotCommandScopeChatMember, + } + + if cls is BotCommandScope and data.get("type") in _class_mapping: + return _class_mapping[data.pop("type")].de_json(data=data, bot=bot) + return super().de_json(data=data, bot=bot) + + +class BotCommandScopeDefault(BotCommandScope): + """Represents the default scope of bot commands. Default commands are used if no commands with + a `narrower scope`_ are specified for the user. + + .. _`narrower scope`: https://core.telegram.org/bots/api#determining-list-of-commands + + .. versionadded:: 13.7 + Attributes: + type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.DEFAULT`. + """ + + __slots__ = () + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(type=BotCommandScope.DEFAULT, api_kwargs=api_kwargs) + self._freeze() + + +class BotCommandScopeAllPrivateChats(BotCommandScope): + """Represents the scope of bot commands, covering all private chats. + + .. versionadded:: 13.7 + + Attributes: + type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.ALL_PRIVATE_CHATS`. + """ + + __slots__ = () + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(type=BotCommandScope.ALL_PRIVATE_CHATS, api_kwargs=api_kwargs) + self._freeze() + + +class BotCommandScopeAllGroupChats(BotCommandScope): + """Represents the scope of bot commands, covering all group and supergroup chats. + + .. versionadded:: 13.7 + Attributes: + type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.ALL_GROUP_CHATS`. + """ + + __slots__ = () + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(type=BotCommandScope.ALL_GROUP_CHATS, api_kwargs=api_kwargs) + self._freeze() + + +class BotCommandScopeAllChatAdministrators(BotCommandScope): + """Represents the scope of bot commands, covering all group and supergroup chat administrators. + + .. versionadded:: 13.7 + Attributes: + type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.ALL_CHAT_ADMINISTRATORS`. + """ + + __slots__ = () + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(type=BotCommandScope.ALL_CHAT_ADMINISTRATORS, api_kwargs=api_kwargs) + self._freeze() + + +class BotCommandScopeChat(BotCommandScope): + """Represents the scope of bot commands, covering a specific chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type` and :attr:`chat_id` are equal. + + .. versionadded:: 13.7 + + Args: + chat_id (:obj:`str` | :obj:`int`): |chat_id_group| + + Attributes: + type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.CHAT`. + chat_id (:obj:`str` | :obj:`int`): |chat_id_group| + """ + + __slots__ = ("chat_id",) + + def __init__(self, chat_id: Union[str, int], *, api_kwargs: Optional[JSONDict] = None): + super().__init__(type=BotCommandScope.CHAT, api_kwargs=api_kwargs) + with self._unfrozen(): + self.chat_id: Union[str, int] = ( + chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) + ) + self._id_attrs = (self.type, self.chat_id) + + +class BotCommandScopeChatAdministrators(BotCommandScope): + """Represents the scope of bot commands, covering all administrators of a specific group or + supergroup chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type` and :attr:`chat_id` are equal. + + .. versionadded:: 13.7 + + Args: + chat_id (:obj:`str` | :obj:`int`): |chat_id_group| + Attributes: + type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.CHAT_ADMINISTRATORS`. + chat_id (:obj:`str` | :obj:`int`): |chat_id_group| + """ + + __slots__ = ("chat_id",) + + def __init__(self, chat_id: Union[str, int], *, api_kwargs: Optional[JSONDict] = None): + super().__init__(type=BotCommandScope.CHAT_ADMINISTRATORS, api_kwargs=api_kwargs) + with self._unfrozen(): + self.chat_id: Union[str, int] = ( + chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) + ) + self._id_attrs = (self.type, self.chat_id) + + +class BotCommandScopeChatMember(BotCommandScope): + """Represents the scope of bot commands, covering a specific member of a group or supergroup + chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type`, :attr:`chat_id` and :attr:`user_id` are equal. + + .. versionadded:: 13.7 + + Args: + chat_id (:obj:`str` | :obj:`int`): |chat_id_group| + user_id (:obj:`int`): Unique identifier of the target user. + + Attributes: + type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.CHAT_MEMBER`. + chat_id (:obj:`str` | :obj:`int`): |chat_id_group| + user_id (:obj:`int`): Unique identifier of the target user. + """ + + __slots__ = ("chat_id", "user_id") + + def __init__( + self, chat_id: Union[str, int], user_id: int, *, api_kwargs: Optional[JSONDict] = None + ): + super().__init__(type=BotCommandScope.CHAT_MEMBER, api_kwargs=api_kwargs) + with self._unfrozen(): + self.chat_id: Union[str, int] = ( + chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) + ) + self.user_id: int = user_id + self._id_attrs = (self.type, self.chat_id, self.user_id) diff --git a/_botdescription.py b/_botdescription.py new file mode 100644 index 0000000000000000000000000000000000000000..e2a9d36df1d5f099327cae91f125e307e024114a --- /dev/null +++ b/_botdescription.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains two objects that represent a Telegram bots (short) description.""" +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class BotDescription(TelegramObject): + """This object represents the bot's description. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`description` is equal. + + .. versionadded:: 20.2 + + Args: + description (:obj:`str`): The bot's description. + + Attributes: + description (:obj:`str`): The bot's description. + + """ + + __slots__ = ("description",) + + def __init__(self, description: str, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + self.description: str = description + + self._id_attrs = (self.description,) + + self._freeze() + + +class BotShortDescription(TelegramObject): + """This object represents the bot's short description. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`short_description` is equal. + + .. versionadded:: 20.2 + + Args: + short_description (:obj:`str`): The bot's short description. + + Attributes: + short_description (:obj:`str`): The bot's short description. + + """ + + __slots__ = ("short_description",) + + def __init__(self, short_description: str, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + self.short_description: str = short_description + + self._id_attrs = (self.short_description,) + + self._freeze() diff --git a/_botname.py b/_botname.py new file mode 100644 index 0000000000000000000000000000000000000000..2a57ea39f0d8770198db1404b3252700dc51ea3c --- /dev/null +++ b/_botname.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represent a Telegram bots name.""" +from typing import Final, Optional + +from telegram import constants +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class BotName(TelegramObject): + """This object represents the bot's name. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`name` is equal. + + .. versionadded:: 20.3 + + Args: + name (:obj:`str`): The bot's name. + + Attributes: + name (:obj:`str`): The bot's name. + + """ + + __slots__ = ("name",) + + def __init__(self, name: str, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + self.name: str = name + + self._id_attrs = (self.name,) + + self._freeze() + + MAX_LENGTH: Final[int] = constants.BotNameLimit.MAX_NAME_LENGTH + """:const:`telegram.constants.BotNameLimit.MAX_NAME_LENGTH`""" diff --git a/_business.py b/_business.py new file mode 100644 index 0000000000000000000000000000000000000000..22c89e024b4dc8967b5237baadc5d478c5eb58a7 --- /dev/null +++ b/_business.py @@ -0,0 +1,455 @@ +#!/usr/bin/env python +# pylint: disable=redefined-builtin +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/] +"""This module contains the Telegram Business related classes.""" + +from datetime import datetime +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._chat import Chat +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.datetime import extract_tzinfo_from_defaults, from_timestamp +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class BusinessConnection(TelegramObject): + """ + Describes the connection of the bot with a business account. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal if their :attr:`id`, :attr:`user`, :attr:`user_chat_id`, :attr:`date`, + :attr:`can_reply`, and :attr:`is_enabled` are equal. + + .. versionadded:: 21.1 + + Args: + id (:obj:`str`): Unique identifier of the business connection. + user (:class:`telegram.User`): Business account user that created the business connection. + user_chat_id (:obj:`int`): Identifier of a private chat with the user who created the + business connection. + date (:obj:`datetime.datetime`): Date the connection was established in Unix time. + can_reply (:obj:`bool`): True, if the bot can act on behalf of the business account in + chats that were active in the last 24 hours. + is_enabled (:obj:`bool`): True, if the connection is active. + + Attributes: + id (:obj:`str`): Unique identifier of the business connection. + user (:class:`telegram.User`): Business account user that created the business connection. + user_chat_id (:obj:`int`): Identifier of a private chat with the user who created the + business connection. + date (:obj:`datetime.datetime`): Date the connection was established in Unix time. + can_reply (:obj:`bool`): True, if the bot can act on behalf of the business account in + chats that were active in the last 24 hours. + is_enabled (:obj:`bool`): True, if the connection is active. + """ + + __slots__ = ( + "can_reply", + "date", + "id", + "is_enabled", + "user", + "user_chat_id", + ) + + def __init__( + self, + id: str, + user: "User", + user_chat_id: int, + date: datetime, + can_reply: bool, + is_enabled: bool, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.id: str = id + self.user: User = user + self.user_chat_id: int = user_chat_id + self.date: datetime = date + self.can_reply: bool = can_reply + self.is_enabled: bool = is_enabled + + self._id_attrs = ( + self.id, + self.user, + self.user_chat_id, + self.date, + self.can_reply, + self.is_enabled, + ) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) + + +class BusinessMessagesDeleted(TelegramObject): + """ + This object is received when messages are deleted from a connected business account. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal if their :attr:`business_connection_id`, :attr:`message_ids`, and + :attr:`chat` are equal. + + .. versionadded:: 21.1 + + Args: + business_connection_id (:obj:`str`): Unique identifier of the business connection. + chat (:class:`telegram.Chat`): Information about a chat in the business account. The bot + may not have access to the chat or the corresponding user. + message_ids (Sequence[:obj:`int`]): A list of identifiers of the deleted messages in the + chat of the business account. + + Attributes: + business_connection_id (:obj:`str`): Unique identifier of the business connection. + chat (:class:`telegram.Chat`): Information about a chat in the business account. The bot + may not have access to the chat or the corresponding user. + message_ids (Tuple[:obj:`int`]): A list of identifiers of the deleted messages in the + chat of the business account. + """ + + __slots__ = ( + "business_connection_id", + "chat", + "message_ids", + ) + + def __init__( + self, + business_connection_id: str, + chat: Chat, + message_ids: Sequence[int], + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.business_connection_id: str = business_connection_id + self.chat: Chat = chat + self.message_ids: Tuple[int, ...] = parse_sequence_arg(message_ids) + + self._id_attrs = ( + self.business_connection_id, + self.chat, + self.message_ids, + ) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) + + +class BusinessIntro(TelegramObject): + """ + This object contains information about the start page settings of a Telegram Business account. + + Objects of this class are comparable in terms of equality. + Two objects of this class are considered equal, if their + :attr:`title`, :attr:`message` and :attr:`sticker` are equal. + + .. versionadded:: 21.1 + + Args: + title (:obj:`str`, optional): Title text of the business intro. + message (:obj:`str`, optional): Message text of the business intro. + sticker (:class:`telegram.Sticker`, optional): Sticker of the business intro. + + Attributes: + title (:obj:`str`): Optional. Title text of the business intro. + message (:obj:`str`): Optional. Message text of the business intro. + sticker (:class:`telegram.Sticker`): Optional. Sticker of the business intro. + """ + + __slots__ = ( + "message", + "sticker", + "title", + ) + + def __init__( + self, + title: Optional[str] = None, + message: Optional[str] = None, + sticker: Optional[Sticker] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.title: Optional[str] = title + self.message: Optional[str] = message + self.sticker: Optional[Sticker] = sticker + + self._id_attrs = (self.title, self.message, self.sticker) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) + + +class BusinessLocation(TelegramObject): + """ + This object contains information about the location of a Telegram Business account. + + Objects of this class are comparable in terms of equality. + Two objects of this class are considered equal, if their + :attr:`address` is equal. + + .. versionadded:: 21.1 + + Args: + address (:obj:`str`): Address of the business. + location (:class:`telegram.Location`, optional): Location of the business. + + Attributes: + address (:obj:`str`): Address of the business. + location (:class:`telegram.Location`): Optional. Location of the business. + """ + + __slots__ = ( + "address", + "location", + ) + + def __init__( + self, + address: str, + location: Optional[Location] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.address: str = address + self.location: Optional[Location] = location + + self._id_attrs = (self.address,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) + + +class BusinessOpeningHoursInterval(TelegramObject): + """ + This object describes an interval of time during which a business is open. + + Objects of this class are comparable in terms of equality. + Two objects of this class are considered equal, if their + :attr:`opening_minute` and :attr:`closing_minute` are equal. + + .. versionadded:: 21.1 + + Examples: + A day has (24 * 60 =) 1440 minutes, a week has (7 * 1440 =) 10080 minutes. + Starting the the minute's sequence from Monday, example values of + :attr:`opening_minute`, :attr:`closing_minute` will map to the following day times: + + * Monday - 8am to 8:30pm: + - ``opening_minute = 480`` :guilabel:`8 * 60` + - ``closing_minute = 1230`` :guilabel:`20 * 60 + 30` + * Tuesday - 24 hours: + - ``opening_minute = 1440`` :guilabel:`24 * 60` + - ``closing_minute = 2879`` :guilabel:`2 * 24 * 60 - 1` + * Sunday - 12am - 11:58pm: + - ``opening_minute = 8640`` :guilabel:`6 * 24 * 60` + - ``closing_minute = 10078`` :guilabel:`7 * 24 * 60 - 2` + + Args: + opening_minute (:obj:`int`): The minute's sequence number in a week, starting on Monday, + marking the start of the time interval during which the business is open; + 0 - 7 * 24 * 60. + closing_minute (:obj:`int`): The minute's + sequence number in a week, starting on Monday, marking the end of the time interval + during which the business is open; 0 - 8 * 24 * 60 + + Attributes: + opening_minute (:obj:`int`): The minute's sequence number in a week, starting on Monday, + marking the start of the time interval during which the business is open; + 0 - 7 * 24 * 60. + closing_minute (:obj:`int`): The minute's + sequence number in a week, starting on Monday, marking the end of the time interval + during which the business is open; 0 - 8 * 24 * 60 + """ + + __slots__ = ("_closing_time", "_opening_time", "closing_minute", "opening_minute") + + def __init__( + self, + opening_minute: int, + closing_minute: int, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.opening_minute: int = opening_minute + self.closing_minute: int = closing_minute + + self._opening_time: Optional[Tuple[int, int, int]] = None + self._closing_time: Optional[Tuple[int, int, int]] = None + + self._id_attrs = (self.opening_minute, self.closing_minute) + + self._freeze() + + def _parse_minute(self, minute: int) -> Tuple[int, int, int]: + return (minute // 1440, minute % 1440 // 60, minute % 1440 % 60) + + @property + def opening_time(self) -> Tuple[int, int, int]: + """Convenience attribute. A :obj:`tuple` parsed from :attr:`opening_minute`. It contains + the `weekday`, `hour` and `minute` in the same ranges as :attr:`datetime.datetime.weekday`, + :attr:`datetime.datetime.hour` and :attr:`datetime.datetime.minute` + + Returns: + Tuple[:obj:`int`, :obj:`int`, :obj:`int`]: + """ + if self._opening_time is None: + self._opening_time = self._parse_minute(self.opening_minute) + return self._opening_time + + @property + def closing_time(self) -> Tuple[int, int, int]: + """Convenience attribute. A :obj:`tuple` parsed from :attr:`closing_minute`. It contains + the `weekday`, `hour` and `minute` in the same ranges as :attr:`datetime.datetime.weekday`, + :attr:`datetime.datetime.hour` and :attr:`datetime.datetime.minute` + + Returns: + Tuple[:obj:`int`, :obj:`int`, :obj:`int`]: + """ + if self._closing_time is None: + self._closing_time = self._parse_minute(self.closing_minute) + return self._closing_time + + +class BusinessOpeningHours(TelegramObject): + """ + This object describes the opening hours of a business. + + Objects of this class are comparable in terms of equality. + Two objects of this class are considered equal, if their + :attr:`time_zone_name` and :attr:`opening_hours` are equal. + + .. versionadded:: 21.1 + + Args: + time_zone_name (:obj:`str`): Unique name of the time zone for which the opening + hours are defined. + opening_hours (Sequence[:class:`telegram.BusinessOpeningHoursInterval`]): List of + time intervals describing business opening hours. + + Attributes: + time_zone_name (:obj:`str`): Unique name of the time zone for which the opening + hours are defined. + opening_hours (Sequence[:class:`telegram.BusinessOpeningHoursInterval`]): List of + time intervals describing business opening hours. + """ + + __slots__ = ("opening_hours", "time_zone_name") + + def __init__( + self, + time_zone_name: str, + opening_hours: Sequence[BusinessOpeningHoursInterval], + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.time_zone_name: str = time_zone_name + self.opening_hours: Sequence[BusinessOpeningHoursInterval] = parse_sequence_arg( + opening_hours + ) + + self._id_attrs = (self.time_zone_name, self.opening_hours) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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 + ) + + return super().de_json(data=data, bot=bot) diff --git a/_callbackquery.py b/_callbackquery.py new file mode 100644 index 0000000000000000000000000000000000000000..bdfa569dbfd04384d62ef756d8506c1359f05100 --- /dev/null +++ b/_callbackquery.py @@ -0,0 +1,892 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=redefined-builtin +"""This module contains an object that represents a Telegram CallbackQuery""" +from typing import TYPE_CHECKING, Final, Optional, Sequence, Tuple, Union + +from telegram import constants +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.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput, ReplyMarkup + +if TYPE_CHECKING: + from telegram import ( + Bot, + GameHighScore, + InlineKeyboardMarkup, + InputMedia, + LinkPreviewOptions, + MessageEntity, + MessageId, + ReplyParameters, + ) + + +class CallbackQuery(TelegramObject): + """ + This object represents an incoming callback query from a callback button in an inline keyboard. + + If the button that originated the query was attached to a message sent by the bot, the field + :attr:`message` will be present. If the button was attached to a message sent via the bot (in + inline mode), the field :attr:`inline_message_id` will be present. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`id` is equal. + + Note: + * In Python :keyword:`from` is a reserved word. Use :paramref:`from_user` instead. + * Exactly one of the fields :attr:`data` or :attr:`game_short_name` will be present. + * After the user presses an inline button, Telegram clients will display a progress bar + until you call :attr:`answer`. It is, therefore, necessary to react + by calling :attr:`telegram.Bot.answer_callback_query` even if no notification to the user + is needed (e.g., without specifying any of the optional parameters). + * If you're using :attr:`telegram.ext.ExtBot.callback_data_cache`, :attr:`data` may be + an instance + of :class:`telegram.ext.InvalidCallbackData`. This will be the case, if the data + associated with the button triggering the :class:`telegram.CallbackQuery` was already + deleted or if :attr:`data` was manipulated by a malicious client. + + .. versionadded:: 13.6 + + Args: + id (:obj:`str`): Unique identifier for this query. + from_user (:class:`telegram.User`): Sender. + chat_instance (:obj:`str`): Global identifier, uniquely corresponding to the chat to which + the message with the callback button was sent. Useful for high scores in games. + message (:class:`telegram.MaybeInaccessibleMessage`, optional): Message sent by the bot + with the callback button that originated the query. + + .. versionchanged:: 20.8 + Accept objects of type :class:`telegram.MaybeInaccessibleMessage` since Bot API 7.0. + data (:obj:`str`, optional): Data associated with the callback button. Be aware that the + message, which originated the query, can contain no callback buttons with this data. + inline_message_id (:obj:`str`, optional): Identifier of the message sent via the bot in + inline mode, that originated the query. + game_short_name (:obj:`str`, optional): Short name of a Game to be returned, serves as + the unique identifier for the game. + + Attributes: + id (:obj:`str`): Unique identifier for this query. + from_user (:class:`telegram.User`): Sender. + chat_instance (:obj:`str`): Global identifier, uniquely corresponding to the chat to which + the message with the callback button was sent. Useful for high scores in games. + message (:class:`telegram.MaybeInaccessibleMessage`): Optional. Message sent by the bot + with the callback button that originated the query. + + .. versionchanged:: 20.8 + Objects may be of type :class:`telegram.MaybeInaccessibleMessage` since Bot API + 7.0. + data (:obj:`str` | :obj:`object`): Optional. Data associated with the callback button. + Be aware that the message, which originated the query, can contain no callback buttons + with this data. + + Tip: + The value here is the same as the value passed in + :paramref:`telegram.InlineKeyboardButton.callback_data`. + inline_message_id (:obj:`str`): Optional. Identifier of the message sent via the bot in + inline mode, that originated the query. + game_short_name (:obj:`str`): Optional. Short name of a Game to be returned, serves as + the unique identifier for the game. + + + """ + + __slots__ = ( + "chat_instance", + "data", + "from_user", + "game_short_name", + "id", + "inline_message_id", + "message", + ) + + def __init__( + self, + id: str, + from_user: User, + chat_instance: str, + message: Optional[MaybeInaccessibleMessage] = None, + data: Optional[str] = None, + inline_message_id: Optional[str] = None, + game_short_name: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.id: str = id + self.from_user: User = from_user + self.chat_instance: str = chat_instance + # Optionals + self.message: Optional[MaybeInaccessibleMessage] = message + self.data: Optional[str] = data + self.inline_message_id: Optional[str] = inline_message_id + self.game_short_name: Optional[str] = game_short_name + + self._id_attrs = (self.id,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) + + async def answer( + self, + text: Optional[str] = None, + show_alert: Optional[bool] = None, + url: Optional[str] = None, + cache_time: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.answer_callback_query(update.callback_query.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.answer_callback_query`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().answer_callback_query( + callback_query_id=self.id, + text=text, + show_alert=show_alert, + url=url, + cache_time=cache_time, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + def _get_message(self, action: str = "edit") -> Message: + """Helper method to get the message for the shortcut methods. Must be called only + if :attr:`inline_message_id` is *not* set. + """ + if not isinstance(self.message, Message): + raise TypeError(f"Cannot {action} an inaccessible message") + return self.message + + async def edit_message_text( + self, + text: str, + parse_mode: ODVInput[str] = DEFAULT_NONE, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + entities: Optional[Sequence["MessageEntity"]] = None, + link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, + *, + disable_web_page_preview: Optional[bool] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union[Message, bool]: + """Shortcut for either:: + + await update.callback_query.message.edit_text(*args, **kwargs) + + or:: + + await bot.edit_message_text( + inline_message_id=update.callback_query.inline_message_id, *args, **kwargs, + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_message_text` and :meth:`telegram.Message.edit_text`. + + .. versionchanged:: 20.8 + Raises :exc:`TypeError` if :attr:`message` is not accessible. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by the bot, the + edited Message is returned, otherwise :obj:`True` is returned. + + Raises: + :exc:`TypeError` if :attr:`message` is not accessible. + + """ + if self.inline_message_id: + return await self.get_bot().edit_message_text( + inline_message_id=self.inline_message_id, + text=text, + parse_mode=parse_mode, + disable_web_page_preview=disable_web_page_preview, + link_preview_options=link_preview_options, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + entities=entities, + chat_id=None, + message_id=None, + # inline messages can not be sent on behalf of a bcid + business_connection_id=None, + ) + return await self._get_message().edit_text( + text=text, + parse_mode=parse_mode, + disable_web_page_preview=disable_web_page_preview, + link_preview_options=link_preview_options, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + entities=entities, + ) + + async def edit_message_caption( + self, + caption: Optional[str] = None, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + show_caption_above_media: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union[Message, bool]: + """Shortcut for either:: + + await update.callback_query.message.edit_caption(*args, **kwargs) + + or:: + + await bot.edit_message_caption( + inline_message_id=update.callback_query.inline_message_id, *args, **kwargs, + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_message_caption` and :meth:`telegram.Message.edit_caption`. + + .. versionchanged:: 20.8 + Raises :exc:`TypeError` if :attr:`message` is not accessible. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by the bot, the + edited Message is returned, otherwise :obj:`True` is returned. + + Raises: + :exc:`TypeError` if :attr:`message` is not accessible. + + """ + if self.inline_message_id: + return await self.get_bot().edit_message_caption( + caption=caption, + inline_message_id=self.inline_message_id, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + parse_mode=parse_mode, + api_kwargs=api_kwargs, + caption_entities=caption_entities, + chat_id=None, + message_id=None, + show_caption_above_media=show_caption_above_media, + # inline messages can not be sent on behalf of a bcid + business_connection_id=None, + ) + return await self._get_message().edit_caption( + caption=caption, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + parse_mode=parse_mode, + api_kwargs=api_kwargs, + caption_entities=caption_entities, + show_caption_above_media=show_caption_above_media, + ) + + async def edit_message_reply_markup( + self, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union[Message, bool]: + """Shortcut for either:: + + await update.callback_query.message.edit_reply_markup(*args, **kwargs) + + or:: + + await bot.edit_message_reply_markup( + inline_message_id=update.callback_query.inline_message_id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_message_reply_markup` and + :meth:`telegram.Message.edit_reply_markup`. + + .. versionchanged:: 20.8 + Raises :exc:`TypeError` if :attr:`message` is not accessible. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by the bot, the + edited Message is returned, otherwise :obj:`True` is returned. + + Raises: + :exc:`TypeError` if :attr:`message` is not accessible. + + """ + if self.inline_message_id: + return await self.get_bot().edit_message_reply_markup( + reply_markup=reply_markup, + inline_message_id=self.inline_message_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + chat_id=None, + message_id=None, + # inline messages can not be sent on behalf of a bcid + business_connection_id=None, + ) + return await self._get_message().edit_reply_markup( + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def edit_message_media( + self, + media: "InputMedia", + reply_markup: Optional["InlineKeyboardMarkup"] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union[Message, bool]: + """Shortcut for either:: + + await update.callback_query.message.edit_media(*args, **kwargs) + + or:: + + await bot.edit_message_media( + inline_message_id=update.callback_query.inline_message_id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_message_media` and :meth:`telegram.Message.edit_media`. + + .. versionchanged:: 20.8 + Raises :exc:`TypeError` if :attr:`message` is not accessible. + + Returns: + :class:`telegram.Message`: On success, if edited message is not an inline message, the + edited Message is returned, otherwise :obj:`True` is returned. + + Raises: + :exc:`TypeError` if :attr:`message` is not accessible. + + """ + if self.inline_message_id: + return await self.get_bot().edit_message_media( + inline_message_id=self.inline_message_id, + media=media, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + chat_id=None, + message_id=None, + # inline messages can not be sent on behalf of a bcid + business_connection_id=None, + ) + return await self._get_message().edit_media( + media=media, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def edit_message_live_location( + self, + latitude: Optional[float] = None, + longitude: Optional[float] = None, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + horizontal_accuracy: Optional[float] = None, + heading: Optional[int] = None, + proximity_alert_radius: Optional[int] = None, + live_period: Optional[int] = None, + *, + location: Optional[Location] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union[Message, bool]: + """Shortcut for either:: + + await update.callback_query.message.edit_live_location(*args, **kwargs) + + or:: + + await bot.edit_message_live_location( + inline_message_id=update.callback_query.inline_message_id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_message_live_location` and + :meth:`telegram.Message.edit_live_location`. + + .. versionchanged:: 20.8 + Raises :exc:`TypeError` if :attr:`message` is not accessible. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by the bot, the + edited Message is returned, otherwise :obj:`True` is returned. + + Raises: + :exc:`TypeError` if :attr:`message` is not accessible. + + """ + if self.inline_message_id: + return await self.get_bot().edit_message_live_location( + inline_message_id=self.inline_message_id, + latitude=latitude, + longitude=longitude, + location=location, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + horizontal_accuracy=horizontal_accuracy, + heading=heading, + proximity_alert_radius=proximity_alert_radius, + live_period=live_period, + chat_id=None, + message_id=None, + # inline messages can not be sent on behalf of a bcid + business_connection_id=None, + ) + return await self._get_message().edit_live_location( + latitude=latitude, + longitude=longitude, + location=location, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + horizontal_accuracy=horizontal_accuracy, + heading=heading, + proximity_alert_radius=proximity_alert_radius, + live_period=live_period, + ) + + async def stop_message_live_location( + self, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union[Message, bool]: + """Shortcut for either:: + + await update.callback_query.message.stop_live_location(*args, **kwargs) + + or:: + + await bot.stop_message_live_location( + inline_message_id=update.callback_query.inline_message_id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.stop_message_live_location` and + :meth:`telegram.Message.stop_live_location`. + + .. versionchanged:: 20.8 + Raises :exc:`TypeError` if :attr:`message` is not accessible. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by the bot, the + edited Message is returned, otherwise :obj:`True` is returned. + + Raises: + :exc:`TypeError` if :attr:`message` is not accessible. + + """ + if self.inline_message_id: + return await self.get_bot().stop_message_live_location( + inline_message_id=self.inline_message_id, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + chat_id=None, + message_id=None, + # inline messages can not be sent on behalf of a bcid + business_connection_id=None, + ) + return await self._get_message().stop_live_location( + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_game_score( + self, + user_id: int, + score: int, + force: Optional[bool] = None, + disable_edit_message: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union[Message, bool]: + """Shortcut for either:: + + await update.callback_query.message.set_game_score(*args, **kwargs) + + or:: + + await bot.set_game_score( + inline_message_id=update.callback_query.inline_message_id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.set_game_score` and :meth:`telegram.Message.set_game_score`. + + .. versionchanged:: 20.8 + Raises :exc:`TypeError` if :attr:`message` is not accessible. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by the bot, the + edited Message is returned, otherwise :obj:`True` is returned. + + Raises: + :exc:`TypeError` if :attr:`message` is not accessible. + + """ + if self.inline_message_id: + return await self.get_bot().set_game_score( + inline_message_id=self.inline_message_id, + user_id=user_id, + score=score, + force=force, + disable_edit_message=disable_edit_message, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + chat_id=None, + message_id=None, + ) + return await self._get_message().set_game_score( + user_id=user_id, + score=score, + force=force, + disable_edit_message=disable_edit_message, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_game_high_scores( + self, + user_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple["GameHighScore", ...]: + """Shortcut for either:: + + await update.callback_query.message.get_game_high_score(*args, **kwargs) + + or:: + + await bot.get_game_high_scores( + inline_message_id=update.callback_query.inline_message_id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.get_game_high_scores` and + :meth:`telegram.Message.get_game_high_scores`. + + .. versionchanged:: 20.8 + Raises :exc:`TypeError` if :attr:`message` is not accessible. + + Returns: + Tuple[:class:`telegram.GameHighScore`] + + Raises: + :exc:`TypeError` if :attr:`message` is not accessible. + + """ + if self.inline_message_id: + return await self.get_bot().get_game_high_scores( + inline_message_id=self.inline_message_id, + user_id=user_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + chat_id=None, + message_id=None, + ) + return await self._get_message().get_game_high_scores( + user_id=user_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_message( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await update.callback_query.message.delete(*args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Message.delete`. + + .. versionchanged:: 20.8 + Raises :exc:`TypeError` if :attr:`message` is not accessible. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :exc:`TypeError` if :attr:`message` is not accessible. + + """ + return await self._get_message(action="delete").delete( + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def pin_message( + self, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await update.callback_query.message.pin(*args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Message.pin`. + + .. versionchanged:: 20.8 + Raises :exc:`TypeError` if :attr:`message` is not accessible. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :exc:`TypeError` if :attr:`message` is not accessible. + """ + return await self._get_message(action="pin").pin( + disable_notification=disable_notification, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unpin_message( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await update.callback_query.message.unpin(*args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Message.unpin`. + + .. versionchanged:: 20.8 + Raises :exc:`TypeError` if :attr:`message` is not accessible. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :exc:`TypeError` if :attr:`message` is not accessible. + """ + return await self._get_message(action="unpin").unpin( + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def copy_message( + self, + chat_id: Union[int, str], + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "MessageId": + """Shortcut for:: + + await update.callback_query.message.copy( + from_chat_id=update.message.chat_id, + message_id=update.message.message_id, + *args, + **kwargs + ) + + For the documentation of the arguments, please see :meth:`telegram.Message.copy`. + + .. versionchanged:: 20.8 + Raises :exc:`TypeError` if :attr:`message` is not accessible. + + Returns: + :class:`telegram.MessageId`: On success, returns the MessageId of the sent message. + + Raises: + :exc:`TypeError` if :attr:`message` is not accessible. + """ + return await self._get_message(action="copy").copy( + chat_id=chat_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + protect_content=protect_content, + message_thread_id=message_thread_id, + reply_parameters=reply_parameters, + show_caption_above_media=show_caption_above_media, + ) + + MAX_ANSWER_TEXT_LENGTH: Final[int] = ( + constants.CallbackQueryLimit.ANSWER_CALLBACK_QUERY_TEXT_LENGTH + ) + """ + :const:`telegram.constants.CallbackQueryLimit.ANSWER_CALLBACK_QUERY_TEXT_LENGTH` + + .. versionadded:: 13.2 + """ diff --git a/_chat.py b/_chat.py new file mode 100644 index 0000000000000000000000000000000000000000..6eb78978596d34df3c5fc66b3c64e9d70db3e661 --- /dev/null +++ b/_chat.py @@ -0,0 +1,3451 @@ +#!/usr/bin/env python +# pylint: disable=redefined-builtin +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram Chat.""" +from datetime import datetime +from html import escape +from typing import TYPE_CHECKING, Final, Optional, Sequence, Tuple, Union + +from telegram import constants +from telegram._chatpermissions import ChatPermissions +from telegram._forumtopic import ForumTopic +from telegram._menubutton import MenuButton +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.helpers import escape_markdown +from telegram.helpers import mention_html as helpers_mention_html +from telegram.helpers import mention_markdown as helpers_mention_markdown + +if TYPE_CHECKING: + from telegram import ( + Animation, + Audio, + ChatInviteLink, + ChatMember, + Contact, + Document, + InlineKeyboardMarkup, + InputMediaAudio, + InputMediaDocument, + InputMediaPhoto, + InputMediaVideo, + InputPaidMedia, + InputPollOption, + LabeledPrice, + LinkPreviewOptions, + Location, + Message, + MessageEntity, + MessageId, + PhotoSize, + ReplyParameters, + Sticker, + UserChatBoosts, + Venue, + Video, + VideoNote, + Voice, + ) + + +class _ChatBase(TelegramObject): + """Base class for :class:`telegram.Chat` and :class:`telegram.ChatFullInfo`. + + .. versionadded:: 21.3 + """ + + __slots__ = ("first_name", "id", "is_forum", "last_name", "title", "type", "username") + + def __init__( + self, + id: int, + type: str, + title: Optional[str] = None, + username: Optional[str] = None, + first_name: Optional[str] = None, + last_name: Optional[str] = None, + is_forum: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.id: int = id + self.type: str = enum.get_member(constants.ChatType, type, type) + # Optionals + self.title: Optional[str] = title + self.username: Optional[str] = username + self.first_name: Optional[str] = first_name + self.last_name: Optional[str] = last_name + self.is_forum: Optional[bool] = is_forum + + self._id_attrs = (self.id,) + + self._freeze() + + SENDER: Final[str] = constants.ChatType.SENDER + """:const:`telegram.constants.ChatType.SENDER` + + .. versionadded:: 13.5 + """ + PRIVATE: Final[str] = constants.ChatType.PRIVATE + """:const:`telegram.constants.ChatType.PRIVATE`""" + GROUP: Final[str] = constants.ChatType.GROUP + """:const:`telegram.constants.ChatType.GROUP`""" + SUPERGROUP: Final[str] = constants.ChatType.SUPERGROUP + """:const:`telegram.constants.ChatType.SUPERGROUP`""" + CHANNEL: Final[str] = constants.ChatType.CHANNEL + """:const:`telegram.constants.ChatType.CHANNEL`""" + + @property + def effective_name(self) -> Optional[str]: + """ + :obj:`str`: Convenience property. Gives :attr:`~Chat.title` if not :obj:`None`, + else :attr:`~Chat.full_name` if not :obj:`None`. + + .. versionadded:: 20.1 + """ + if self.title is not None: + return self.title + if self.full_name is not None: + return self.full_name + return None + + @property + def full_name(self) -> Optional[str]: + """ + :obj:`str`: Convenience property. If :attr:`~Chat.first_name` is not :obj:`None`, gives + :attr:`~Chat.first_name` followed by (if available) :attr:`~Chat.last_name`. + + Note: + :attr:`full_name` will always be :obj:`None`, if the chat is a (super)group or + channel. + + .. versionadded:: 13.2 + """ + if not self.first_name: + return None + if self.last_name: + return f"{self.first_name} {self.last_name}" + return self.first_name + + @property + def link(self) -> Optional[str]: + """:obj:`str`: Convenience property. If the chat has a :attr:`~Chat.username`, returns a + t.me link of the chat. + """ + if self.username: + return f"https://t.me/{self.username}" + return None + + def mention_markdown(self, name: Optional[str] = None) -> str: + """ + Note: + :tg-const:`telegram.constants.ParseMode.MARKDOWN` is a legacy mode, retained by + Telegram for backward compatibility. You should use :meth:`mention_markdown_v2` + instead. + + .. versionadded:: 20.0 + + Args: + name (:obj:`str`): The name used as a link for the chat. Defaults to + :attr:`~Chat.full_name`. + + Returns: + :obj:`str`: The inline mention for the chat as markdown (version 1). + + Raises: + :exc:`TypeError`: If the chat is a private chat and neither the :paramref:`name` + nor the :attr:`~Chat.first_name` is set, then throw an :exc:`TypeError`. + If the chat is a public chat and neither the :paramref:`name` nor the + :attr:`~Chat.title` is set, then throw an :exc:`TypeError`. If chat is a + private group chat, then throw an :exc:`TypeError`. + + """ + if self.type == self.PRIVATE: + if name: + return helpers_mention_markdown(self.id, name) + if self.full_name: + return helpers_mention_markdown(self.id, self.full_name) + raise TypeError("Can not create a mention to a private chat without first name") + if self.username: + if name: + return f"[{name}]({self.link})" + if self.title: + return f"[{self.title}]({self.link})" + raise TypeError("Can not create a mention to a public chat without title") + raise TypeError("Can not create a mention to a private group chat") + + def mention_markdown_v2(self, name: Optional[str] = None) -> str: + """ + .. versionadded:: 20.0 + + Args: + name (:obj:`str`): The name used as a link for the chat. Defaults to + :attr:`~Chat.full_name`. + + Returns: + :obj:`str`: The inline mention for the chat as markdown (version 2). + + Raises: + :exc:`TypeError`: If the chat is a private chat and neither the :paramref:`name` + nor the :attr:`~Chat.first_name` is set, then throw an :exc:`TypeError`. + If the chat is a public chat and neither the :paramref:`name` nor the + :attr:`~Chat.title` is set, then throw an :exc:`TypeError`. If chat is a + private group chat, then throw an :exc:`TypeError`. + + """ + if self.type == self.PRIVATE: + if name: + return helpers_mention_markdown(self.id, name, version=2) + if self.full_name: + return helpers_mention_markdown(self.id, self.full_name, version=2) + raise TypeError("Can not create a mention to a private chat without first name") + if self.username: + if name: + return f"[{escape_markdown(name, version=2)}]({self.link})" + if self.title: + return f"[{escape_markdown(self.title, version=2)}]({self.link})" + raise TypeError("Can not create a mention to a public chat without title") + raise TypeError("Can not create a mention to a private group chat") + + def mention_html(self, name: Optional[str] = None) -> str: + """ + .. versionadded:: 20.0 + + Args: + name (:obj:`str`): The name used as a link for the chat. Defaults to :attr:`full_name`. + + Returns: + :obj:`str`: The inline mention for the chat as HTML. + + Raises: + :exc:`TypeError`: If the chat is a private chat and neither the :paramref:`name` + nor the :attr:`~Chat.first_name` is set, then throw an :exc:`TypeError`. + If the chat is a public chat and neither the :paramref:`name` nor the + :attr:`~Chat.title` is set, then throw an :exc:`TypeError`. + If chat is a private group chat, then throw an :exc:`TypeError`. + + """ + if self.type == self.PRIVATE: + if name: + return helpers_mention_html(self.id, name) + if self.full_name: + return helpers_mention_html(self.id, self.full_name) + raise TypeError("Can not create a mention to a private chat without first name") + if self.username: + if name: + return f'{escape(name)}' + if self.title: + return f'{escape(self.title)}' + raise TypeError("Can not create a mention to a public chat without title") + raise TypeError("Can not create a mention to a private group chat") + + async def leave( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.leave_chat(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.leave_chat`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().leave_chat( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_administrators( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple["ChatMember", ...]: + """Shortcut for:: + + await bot.get_chat_administrators(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.get_chat_administrators`. + + Returns: + Tuple[:class:`telegram.ChatMember`]: A tuple of administrators in a chat. An Array of + :class:`telegram.ChatMember` objects that contains information about all + chat administrators except other bots. If the chat is a group or a supergroup + and no administrators were appointed, only the creator will be returned. + + """ + return await self.get_bot().get_chat_administrators( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_member_count( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> int: + """Shortcut for:: + + await bot.get_chat_member_count(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.get_chat_member_count`. + + Returns: + :obj:`int` + """ + return await self.get_bot().get_chat_member_count( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_member( + self, + user_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "ChatMember": + """Shortcut for:: + + await bot.get_chat_member(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.get_chat_member`. + + Returns: + :class:`telegram.ChatMember` + + """ + return await self.get_bot().get_chat_member( + chat_id=self.id, + user_id=user_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def ban_member( + self, + user_id: int, + revoke_messages: Optional[bool] = None, + until_date: Optional[Union[int, datetime]] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.ban_chat_member(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.ban_chat_member`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().ban_chat_member( + chat_id=self.id, + user_id=user_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + until_date=until_date, + api_kwargs=api_kwargs, + revoke_messages=revoke_messages, + ) + + async def ban_sender_chat( + self, + sender_chat_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.ban_chat_sender_chat(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.ban_chat_sender_chat`. + + .. versionadded:: 13.9 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().ban_chat_sender_chat( + chat_id=self.id, + sender_chat_id=sender_chat_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def ban_chat( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.ban_chat_sender_chat( + sender_chat_id=update.effective_chat.id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.ban_chat_sender_chat`. + + .. versionadded:: 13.9 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().ban_chat_sender_chat( + chat_id=chat_id, + sender_chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unban_sender_chat( + self, + sender_chat_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.unban_chat_sender_chat(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.unban_chat_sender_chat`. + + .. versionadded:: 13.9 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().unban_chat_sender_chat( + chat_id=self.id, + sender_chat_id=sender_chat_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unban_chat( + self, + chat_id: Union[str, int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.unban_chat_sender_chat( + sender_chat_id=update.effective_chat.id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.unban_chat_sender_chat`. + + .. versionadded:: 13.9 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().unban_chat_sender_chat( + chat_id=chat_id, + sender_chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unban_member( + self, + user_id: int, + only_if_banned: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.unban_chat_member(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.unban_chat_member`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().unban_chat_member( + chat_id=self.id, + user_id=user_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + only_if_banned=only_if_banned, + ) + + async def promote_member( + self, + user_id: int, + can_change_info: Optional[bool] = None, + can_post_messages: Optional[bool] = None, + can_edit_messages: Optional[bool] = None, + can_delete_messages: Optional[bool] = None, + can_invite_users: Optional[bool] = None, + can_restrict_members: Optional[bool] = None, + can_pin_messages: Optional[bool] = None, + can_promote_members: Optional[bool] = None, + is_anonymous: Optional[bool] = None, + can_manage_chat: Optional[bool] = None, + can_manage_video_chats: Optional[bool] = None, + can_manage_topics: Optional[bool] = None, + can_post_stories: Optional[bool] = None, + can_edit_stories: Optional[bool] = None, + can_delete_stories: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.promote_chat_member(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.promote_chat_member`. + + .. versionadded:: 13.2 + .. versionchanged:: 20.0 + The argument ``can_manage_voice_chats`` was renamed to + :paramref:`~telegram.Bot.promote_chat_member.can_manage_video_chats` in accordance to + Bot API 6.0. + .. versionchanged:: 20.6 + The arguments `can_post_stories`, `can_edit_stories` and `can_delete_stories` were + added. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().promote_chat_member( + chat_id=self.id, + user_id=user_id, + can_change_info=can_change_info, + can_post_messages=can_post_messages, + can_edit_messages=can_edit_messages, + can_delete_messages=can_delete_messages, + can_invite_users=can_invite_users, + can_restrict_members=can_restrict_members, + can_pin_messages=can_pin_messages, + can_promote_members=can_promote_members, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + is_anonymous=is_anonymous, + can_manage_chat=can_manage_chat, + can_manage_video_chats=can_manage_video_chats, + can_manage_topics=can_manage_topics, + can_post_stories=can_post_stories, + can_edit_stories=can_edit_stories, + can_delete_stories=can_delete_stories, + ) + + async def restrict_member( + self, + user_id: int, + permissions: ChatPermissions, + until_date: Optional[Union[int, datetime]] = None, + use_independent_chat_permissions: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.restrict_chat_member(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.restrict_chat_member`. + + .. versionadded:: 13.2 + + .. versionadded:: 20.1 + Added :paramref:`~telegram.Bot.restrict_chat_member.use_independent_chat_permissions`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().restrict_chat_member( + chat_id=self.id, + user_id=user_id, + permissions=permissions, + until_date=until_date, + use_independent_chat_permissions=use_independent_chat_permissions, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_permissions( + self, + permissions: ChatPermissions, + use_independent_chat_permissions: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.set_chat_permissions(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.set_chat_permissions`. + + .. versionadded:: 20.1 + Added :paramref:`~telegram.Bot.set_chat_permissions.use_independent_chat_permissions`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().set_chat_permissions( + chat_id=self.id, + permissions=permissions, + use_independent_chat_permissions=use_independent_chat_permissions, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_administrator_custom_title( + self, + user_id: int, + custom_title: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.set_chat_administrator_custom_title( + update.effective_chat.id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.set_chat_administrator_custom_title`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().set_chat_administrator_custom_title( + chat_id=self.id, + user_id=user_id, + custom_title=custom_title, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_photo( + self, + photo: FileInput, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.set_chat_photo( + chat_id=update.effective_chat.id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.set_chat_photo`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().set_chat_photo( + chat_id=self.id, + photo=photo, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_photo( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.delete_chat_photo( + chat_id=update.effective_chat.id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.delete_chat_photo`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().delete_chat_photo( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_title( + self, + title: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.set_chat_title( + chat_id=update.effective_chat.id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.set_chat_title`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().set_chat_title( + chat_id=self.id, + title=title, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_description( + self, + description: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.set_chat_description( + chat_id=update.effective_chat.id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.set_chat_description`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().set_chat_description( + chat_id=self.id, + description=description, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def pin_message( + self, + message_id: int, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + business_connection_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.pin_chat_message(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.pin_chat_message`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().pin_chat_message( + chat_id=self.id, + message_id=message_id, + disable_notification=disable_notification, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + ) + + async def unpin_message( + self, + message_id: Optional[int] = None, + business_connection_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.unpin_chat_message(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.unpin_chat_message`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().unpin_chat_message( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + message_id=message_id, + business_connection_id=business_connection_id, + ) + + async def unpin_all_messages( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.unpin_all_chat_messages(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.unpin_all_chat_messages`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().unpin_all_chat_messages( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def send_message( + self, + text: str, + parse_mode: ODVInput[str] = DEFAULT_NONE, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + disable_web_page_preview: Optional[bool] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_message(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_message`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_message( + chat_id=self.id, + text=text, + parse_mode=parse_mode, + disable_web_page_preview=disable_web_page_preview, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + link_preview_options=link_preview_options, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + entities=entities, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def delete_message( + self, + message_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.delete_message(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.delete_message`. + + .. versionadded:: 20.8 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().delete_message( + chat_id=self.id, + message_id=message_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_messages( + self, + message_ids: Sequence[int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.delete_messages(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.delete_messages`. + + .. versionadded:: 20.8 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().delete_messages( + chat_id=self.id, + message_ids=message_ids, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def send_media_group( + self, + media: Sequence[ + Union["InputMediaAudio", "InputMediaDocument", "InputMediaPhoto", "InputMediaVideo"] + ], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + ) -> Tuple["Message", ...]: + """Shortcut for:: + + await bot.send_media_group(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_media_group`. + + Returns: + Tuple[:class:`telegram.Message`]: On success, a tuple of :class:`~telegram.Message` + instances that were sent is returned. + + """ + return await self.get_bot().send_media_group( + chat_id=self.id, + media=media, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + reply_parameters=reply_parameters, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_chat_action( + self, + action: str, + message_thread_id: Optional[int] = None, + business_connection_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.send_chat_action(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_chat_action`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().send_chat_action( + chat_id=self.id, + action=action, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + ) + + send_action = send_chat_action + """Alias for :attr:`send_chat_action`""" + + async def send_photo( + self, + photo: Union[FileInput, "PhotoSize"], + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + has_spoiler: Optional[bool] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_photo(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_photo`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_photo( + chat_id=self.id, + photo=photo, + caption=caption, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + parse_mode=parse_mode, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + has_spoiler=has_spoiler, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, + ) + + async def send_contact( + self, + phone_number: Optional[str] = None, + first_name: Optional[str] = None, + last_name: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + vcard: Optional[str] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + contact: Optional["Contact"] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_contact(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_contact`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_contact( + chat_id=self.id, + phone_number=phone_number, + first_name=first_name, + last_name=last_name, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + contact=contact, + vcard=vcard, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_audio( + self, + audio: Union[FileInput, "Audio"], + duration: Optional[int] = None, + performer: Optional[str] = None, + title: Optional[str] = None, + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_audio(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_audio`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_audio( + chat_id=self.id, + audio=audio, + duration=duration, + performer=performer, + title=title, + caption=caption, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + parse_mode=parse_mode, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + thumbnail=thumbnail, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_document( + self, + document: Union[FileInput, "Document"], + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + disable_content_type_detection: Optional[bool] = None, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_document(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_document`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_document( + chat_id=self.id, + document=document, + filename=filename, + caption=caption, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + parse_mode=parse_mode, + thumbnail=thumbnail, + api_kwargs=api_kwargs, + disable_content_type_detection=disable_content_type_detection, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_dice( + self, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + emoji: Optional[str] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_dice(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_dice`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_dice( + chat_id=self.id, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + emoji=emoji, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_game( + self, + game_short_name: str, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_game(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_game`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_game( + chat_id=self.id, + game_short_name=game_short_name, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_invoice( + self, + title: str, + description: str, + payload: str, + provider_token: Optional[str], + currency: str, + prices: Sequence["LabeledPrice"], + start_parameter: Optional[str] = None, + photo_url: Optional[str] = None, + photo_size: Optional[int] = None, + photo_width: Optional[int] = None, + photo_height: Optional[int] = None, + need_name: Optional[bool] = None, + need_phone_number: Optional[bool] = None, + need_email: Optional[bool] = None, + need_shipping_address: Optional[bool] = None, + is_flexible: Optional[bool] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + provider_data: Optional[Union[str, object]] = None, + send_phone_number_to_provider: Optional[bool] = None, + send_email_to_provider: Optional[bool] = None, + max_tip_amount: Optional[int] = None, + suggested_tip_amounts: Optional[Sequence[int]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_invoice(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_invoice`. + + Warning: + As of API 5.2 :paramref:`start_parameter ` + is an optional argument and therefore the + order of the arguments had to be changed. Use keyword arguments to make sure that the + arguments are passed correctly. + + .. versionchanged:: 13.5 + As of Bot API 5.2, the parameter + :paramref:`start_parameter ` is optional. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_invoice( + chat_id=self.id, + title=title, + description=description, + payload=payload, + provider_token=provider_token, + currency=currency, + prices=prices, + start_parameter=start_parameter, + photo_url=photo_url, + photo_size=photo_size, + photo_width=photo_width, + photo_height=photo_height, + need_name=need_name, + need_phone_number=need_phone_number, + need_email=need_email, + need_shipping_address=need_shipping_address, + is_flexible=is_flexible, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup, + provider_data=provider_data, + send_phone_number_to_provider=send_phone_number_to_provider, + send_email_to_provider=send_email_to_provider, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + max_tip_amount=max_tip_amount, + suggested_tip_amounts=suggested_tip_amounts, + protect_content=protect_content, + message_thread_id=message_thread_id, + reply_parameters=reply_parameters, + message_effect_id=message_effect_id, + ) + + async def send_location( + self, + latitude: Optional[float] = None, + longitude: Optional[float] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + live_period: Optional[int] = None, + horizontal_accuracy: Optional[float] = None, + heading: Optional[int] = None, + proximity_alert_radius: Optional[int] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + location: Optional["Location"] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_location(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_location`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_location( + chat_id=self.id, + latitude=latitude, + longitude=longitude, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + location=location, + live_period=live_period, + api_kwargs=api_kwargs, + horizontal_accuracy=horizontal_accuracy, + heading=heading, + proximity_alert_radius=proximity_alert_radius, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_animation( + self, + animation: Union[FileInput, "Animation"], + duration: Optional[int] = None, + width: Optional[int] = None, + height: Optional[int] = None, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + has_spoiler: Optional[bool] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_animation(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_animation`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_animation( + chat_id=self.id, + animation=animation, + duration=duration, + width=width, + height=height, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + has_spoiler=has_spoiler, + thumbnail=thumbnail, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, + ) + + async def send_sticker( + self, + sticker: Union[FileInput, "Sticker"], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + emoji: Optional[str] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_sticker(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_sticker`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_sticker( + chat_id=self.id, + sticker=sticker, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + emoji=emoji, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_venue( + self, + latitude: Optional[float] = None, + longitude: Optional[float] = None, + title: Optional[str] = None, + address: Optional[str] = None, + foursquare_id: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + foursquare_type: Optional[str] = None, + google_place_id: Optional[str] = None, + google_place_type: Optional[str] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + venue: Optional["Venue"] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_venue(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_venue`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_venue( + chat_id=self.id, + latitude=latitude, + longitude=longitude, + title=title, + address=address, + foursquare_id=foursquare_id, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + venue=venue, + foursquare_type=foursquare_type, + api_kwargs=api_kwargs, + google_place_id=google_place_id, + google_place_type=google_place_type, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_video( + self, + video: Union[FileInput, "Video"], + duration: Optional[int] = None, + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + width: Optional[int] = None, + height: Optional[int] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + supports_streaming: Optional[bool] = None, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + has_spoiler: Optional[bool] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_video(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_video`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_video( + chat_id=self.id, + video=video, + duration=duration, + caption=caption, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + width=width, + height=height, + parse_mode=parse_mode, + supports_streaming=supports_streaming, + thumbnail=thumbnail, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + has_spoiler=has_spoiler, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, + ) + + async def send_video_note( + self, + video_note: Union[FileInput, "VideoNote"], + duration: Optional[int] = None, + length: Optional[int] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_video_note(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_video_note`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_video_note( + chat_id=self.id, + video_note=video_note, + duration=duration, + length=length, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + thumbnail=thumbnail, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_voice( + self, + voice: Union[FileInput, "Voice"], + duration: Optional[int] = None, + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_voice(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_voice`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_voice( + chat_id=self.id, + voice=voice, + duration=duration, + caption=caption, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + parse_mode=parse_mode, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_poll( + self, + question: str, + options: Sequence[Union[str, "InputPollOption"]], + is_anonymous: Optional[bool] = None, + type: Optional[str] = None, + allows_multiple_answers: Optional[bool] = None, + correct_option_id: Optional[CorrectOptionID] = None, + is_closed: Optional[bool] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + explanation: Optional[str] = None, + explanation_parse_mode: ODVInput[str] = DEFAULT_NONE, + open_period: Optional[int] = None, + close_date: Optional[Union[int, datetime]] = None, + explanation_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + question_parse_mode: ODVInput[str] = DEFAULT_NONE, + question_entities: Optional[Sequence["MessageEntity"]] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_poll(update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_poll`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_poll( + chat_id=self.id, + question=question, + options=options, + is_anonymous=is_anonymous, + type=type, # pylint=pylint, + allows_multiple_answers=allows_multiple_answers, + correct_option_id=correct_option_id, + is_closed=is_closed, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + message_effect_id=message_effect_id, + explanation=explanation, + explanation_parse_mode=explanation_parse_mode, + open_period=open_period, + close_date=close_date, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + explanation_entities=explanation_entities, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + question_parse_mode=question_parse_mode, + question_entities=question_entities, + ) + + async def send_copy( + self, + from_chat_id: Union[str, int], + message_id: int, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "MessageId": + """Shortcut for:: + + await bot.copy_message(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.copy_message`. + + .. seealso:: :meth:`copy_message`, :meth:`send_copies`, :meth:`copy_messages`. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().copy_message( + chat_id=self.id, + from_chat_id=from_chat_id, + message_id=message_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + allow_sending_without_reply=allow_sending_without_reply, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + protect_content=protect_content, + message_thread_id=message_thread_id, + show_caption_above_media=show_caption_above_media, + ) + + async def copy_message( + self, + chat_id: Union[int, str], + message_id: int, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "MessageId": + """Shortcut for:: + + await bot.copy_message(from_chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.copy_message`. + + .. seealso:: :meth:`send_copy`, :meth:`send_copies`, :meth:`copy_messages`. + + Returns: + :class:`telegram.MessageId`: On success, returns the MessageId of the sent message. + + """ + return await self.get_bot().copy_message( + from_chat_id=self.id, + chat_id=chat_id, + message_id=message_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + allow_sending_without_reply=allow_sending_without_reply, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + protect_content=protect_content, + message_thread_id=message_thread_id, + show_caption_above_media=show_caption_above_media, + ) + + async def send_copies( + self, + from_chat_id: Union[str, int], + message_ids: Sequence[int], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + remove_caption: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple["MessageId", ...]: + """Shortcut for:: + + await bot.copy_messages(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.copy_messages`. + + .. seealso:: :meth:`copy_message`, :meth:`send_copy`, :meth:`copy_messages`. + + .. versionadded:: 20.8 + + Returns: + Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + of the sent messages is returned. + + """ + return await self.get_bot().copy_messages( + chat_id=self.id, + from_chat_id=from_chat_id, + message_ids=message_ids, + disable_notification=disable_notification, + protect_content=protect_content, + message_thread_id=message_thread_id, + remove_caption=remove_caption, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def copy_messages( + self, + chat_id: Union[str, int], + message_ids: Sequence[int], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + remove_caption: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple["MessageId", ...]: + """Shortcut for:: + + await bot.copy_messages(from_chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.copy_messages`. + + .. seealso:: :meth:`copy_message`, :meth:`send_copy`, :meth:`send_copies`. + + .. versionadded:: 20.8 + + Returns: + Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + of the sent messages is returned. + + """ + return await self.get_bot().copy_messages( + from_chat_id=self.id, + chat_id=chat_id, + message_ids=message_ids, + disable_notification=disable_notification, + protect_content=protect_content, + message_thread_id=message_thread_id, + remove_caption=remove_caption, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def forward_from( + self, + from_chat_id: Union[str, int], + message_id: int, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.forward_message(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.forward_message`. + + .. seealso:: :meth:`forward_to`, :meth:`forward_messages_from`, :meth:`forward_messages_to` + + .. versionadded:: 20.0 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().forward_message( + chat_id=self.id, + from_chat_id=from_chat_id, + message_id=message_id, + disable_notification=disable_notification, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + protect_content=protect_content, + message_thread_id=message_thread_id, + ) + + async def forward_to( + self, + chat_id: Union[int, str], + message_id: int, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.forward_message(from_chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.forward_message`. + + .. seealso:: :meth:`forward_from`, :meth:`forward_messages_from`, + :meth:`forward_messages_to` + + .. versionadded:: 20.0 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().forward_message( + from_chat_id=self.id, + chat_id=chat_id, + message_id=message_id, + disable_notification=disable_notification, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + protect_content=protect_content, + message_thread_id=message_thread_id, + ) + + async def forward_messages_from( + self, + from_chat_id: Union[str, int], + message_ids: Sequence[int], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple["MessageId", ...]: + """Shortcut for:: + + await bot.forward_messages(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.forward_messages`. + + .. seealso:: :meth:`forward_to`, :meth:`forward_from`, :meth:`forward_messages_to`. + + .. versionadded:: 20.8 + + Returns: + Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + of sent messages is returned. + + """ + return await self.get_bot().forward_messages( + chat_id=self.id, + from_chat_id=from_chat_id, + message_ids=message_ids, + disable_notification=disable_notification, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def forward_messages_to( + self, + chat_id: Union[int, str], + message_ids: Sequence[int], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple["MessageId", ...]: + """Shortcut for:: + + await bot.forward_messages(from_chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.forward_messages`. + + .. seealso:: :meth:`forward_from`, :meth:`forward_to`, :meth:`forward_messages_from`. + + .. versionadded:: 20.8 + + Returns: + Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + of sent messages is returned. + + """ + return await self.get_bot().forward_messages( + from_chat_id=self.id, + chat_id=chat_id, + message_ids=message_ids, + disable_notification=disable_notification, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def export_invite_link( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> str: + """Shortcut for:: + + await bot.export_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.export_chat_invite_link`. + + .. versionadded:: 13.4 + + Returns: + :obj:`str`: New invite link on success. + + """ + return await self.get_bot().export_chat_invite_link( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def create_invite_link( + self, + expire_date: Optional[Union[int, datetime]] = None, + member_limit: Optional[int] = None, + name: Optional[str] = None, + creates_join_request: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "ChatInviteLink": + """Shortcut for:: + + await bot.create_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.create_chat_invite_link`. + + .. versionadded:: 13.4 + + .. versionchanged:: 13.8 + Edited signature according to the changes of + :meth:`telegram.Bot.create_chat_invite_link`. + + Returns: + :class:`telegram.ChatInviteLink` + + """ + return await self.get_bot().create_chat_invite_link( + chat_id=self.id, + expire_date=expire_date, + member_limit=member_limit, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + name=name, + creates_join_request=creates_join_request, + ) + + async def edit_invite_link( + self, + invite_link: Union[str, "ChatInviteLink"], + expire_date: Optional[Union[int, datetime]] = None, + member_limit: Optional[int] = None, + name: Optional[str] = None, + creates_join_request: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "ChatInviteLink": + """Shortcut for:: + + await bot.edit_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_chat_invite_link`. + + .. versionadded:: 13.4 + + .. versionchanged:: 13.8 + Edited signature according to the changes of :meth:`telegram.Bot.edit_chat_invite_link`. + + Returns: + :class:`telegram.ChatInviteLink` + + """ + return await self.get_bot().edit_chat_invite_link( + chat_id=self.id, + invite_link=invite_link, + expire_date=expire_date, + member_limit=member_limit, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + name=name, + creates_join_request=creates_join_request, + ) + + async def revoke_invite_link( + self, + invite_link: Union[str, "ChatInviteLink"], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "ChatInviteLink": + """Shortcut for:: + + await bot.revoke_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.revoke_chat_invite_link`. + + .. versionadded:: 13.4 + + Returns: + :class:`telegram.ChatInviteLink` + + """ + return await self.get_bot().revoke_chat_invite_link( + chat_id=self.id, + invite_link=invite_link, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def create_subscription_invite_link( + self, + subscription_period: int, + subscription_price: int, + name: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "ChatInviteLink": + """Shortcut for:: + + await bot.create_chat_subscription_invite_link( + chat_id=update.effective_chat.id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.create_chat_subscription_invite_link`. + + .. versionadded:: 21.5 + + Returns: + :class:`telegram.ChatInviteLink` + """ + return await self.get_bot().create_chat_subscription_invite_link( + chat_id=self.id, + subscription_period=subscription_period, + subscription_price=subscription_price, + name=name, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def edit_subscription_invite_link( + self, + invite_link: Union[str, "ChatInviteLink"], + name: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "ChatInviteLink": + """Shortcut for:: + + await bot.edit_chat_subscription_invite_link( + chat_id=update.effective_chat.id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_chat_subscription_invite_link`. + + .. versionadded:: 21.5 + + Returns: + :class:`telegram.ChatInviteLink` + + """ + return await self.get_bot().edit_chat_subscription_invite_link( + chat_id=self.id, + invite_link=invite_link, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + name=name, + ) + + async def approve_join_request( + self, + user_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.approve_chat_join_request(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.approve_chat_join_request`. + + .. versionadded:: 13.8 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().approve_chat_join_request( + chat_id=self.id, + user_id=user_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def decline_join_request( + self, + user_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.decline_chat_join_request(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.decline_chat_join_request`. + + .. versionadded:: 13.8 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().decline_chat_join_request( + chat_id=self.id, + user_id=user_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_menu_button( + self, + menu_button: Optional[MenuButton] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.set_chat_menu_button(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.set_chat_menu_button`. + + Caution: + Can only work, if the chat is a private chat. + + .. seealso:: :meth:`get_menu_button` + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().set_chat_menu_button( + chat_id=self.id, + menu_button=menu_button, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def create_forum_topic( + self, + name: str, + icon_color: Optional[int] = None, + icon_custom_emoji_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> ForumTopic: + """Shortcut for:: + + await bot.create_forum_topic(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.create_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :class:`telegram.ForumTopic` + """ + return await self.get_bot().create_forum_topic( + chat_id=self.id, + name=name, + icon_color=icon_color, + icon_custom_emoji_id=icon_custom_emoji_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def edit_forum_topic( + self, + message_thread_id: int, + name: Optional[str] = None, + icon_custom_emoji_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.edit_forum_topic(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().edit_forum_topic( + chat_id=self.id, + message_thread_id=message_thread_id, + name=name, + icon_custom_emoji_id=icon_custom_emoji_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def close_forum_topic( + self, + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.close_forum_topic(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.close_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().close_forum_topic( + chat_id=self.id, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def reopen_forum_topic( + self, + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.reopen_forum_topic(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.reopen_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().reopen_forum_topic( + chat_id=self.id, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_forum_topic( + self, + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.delete_forum_topic(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.delete_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().delete_forum_topic( + chat_id=self.id, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unpin_all_forum_topic_messages( + self, + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.unpin_all_forum_topic_messages(chat_id=update.effective_chat.id, + *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.unpin_all_forum_topic_messages`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().unpin_all_forum_topic_messages( + chat_id=self.id, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unpin_all_general_forum_topic_messages( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.unpin_all_general_forum_topic_messages(chat_id=update.effective_chat.id, + *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.unpin_all_general_forum_topic_messages`. + + .. versionadded:: 20.5 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().unpin_all_general_forum_topic_messages( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def edit_general_forum_topic( + self, + name: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.edit_general_forum_topic( + chat_id=update.effective_chat.id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_general_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().edit_general_forum_topic( + chat_id=self.id, + name=name, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def close_general_forum_topic( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.close_general_forum_topic(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.close_general_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().close_general_forum_topic( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def reopen_general_forum_topic( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.reopen_general_forum_topic( + chat_id=update.effective_chat.id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.reopen_general_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().reopen_general_forum_topic( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def hide_general_forum_topic( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.hide_general_forum_topic(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.hide_general_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().hide_general_forum_topic( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unhide_general_forum_topic( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.unhide_general_forum_topic ( + chat_id=update.effective_chat.id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.unhide_general_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().unhide_general_forum_topic( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_menu_button( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> MenuButton: + """Shortcut for:: + + await bot.get_chat_menu_button(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.get_chat_menu_button`. + + Caution: + Can only work, if the chat is a private chat. + + .. seealso:: :meth:`set_menu_button` + + .. versionadded:: 20.0 + + Returns: + :class:`telegram.MenuButton`: On success, the current menu button is returned. + """ + return await self.get_bot().get_chat_menu_button( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_user_chat_boosts( + self, + user_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "UserChatBoosts": + """Shortcut for:: + + await bot.get_user_chat_boosts(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.get_user_chat_boosts`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.UserChatBoosts`: On success, returns the boosts applied in the chat. + """ + return await self.get_bot().get_user_chat_boosts( + chat_id=self.id, + user_id=user_id, + api_kwargs=api_kwargs, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + ) + + async def set_message_reaction( + self, + message_id: int, + reaction: Optional[Union[Sequence[Union[ReactionType, str]], ReactionType, str]] = None, + is_big: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.set_message_reaction(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.set_message_reaction`. + + .. versionadded:: 20.8 + + Returns: + :obj:`bool` On success, :obj:`True` is returned. + """ + return await self.get_bot().set_message_reaction( + chat_id=self.id, + message_id=message_id, + reaction=reaction, + is_big=is_big, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def send_paid_media( + self, + star_count: int, + media: Sequence["InputPaidMedia"], + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + show_caption_above_media: Optional[bool] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + reply_markup: Optional[ReplyMarkup] = None, + business_connection_id: Optional[str] = None, + *, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + reply_to_message_id: Optional[int] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_paid_media(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.send_paid_media`. + + .. versionadded:: 21.4 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + """ + return await self.get_bot().send_paid_media( + chat_id=self.id, + star_count=star_count, + media=media, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + show_caption_above_media=show_caption_above_media, + disable_notification=disable_notification, + protect_content=protect_content, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + reply_to_message_id=reply_to_message_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + ) + + +class Chat(_ChatBase): + """This object represents a chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`id` is equal. + + .. versionchanged:: 20.0 + + * Removed the deprecated methods ``kick_member`` and ``get_members_count``. + * The following are now keyword-only arguments in Bot methods: + ``location``, ``filename``, ``contact``, ``{read, write, connect, pool}_timeout``, + ``api_kwargs``. Use a named argument for those, + and notice that some positional arguments changed position as a result. + + .. versionchanged:: 20.0 + Removed the attribute ``all_members_are_administrators``. As long as Telegram provides + this field for backwards compatibility, it is available through + :attr:`~telegram.TelegramObject.api_kwargs`. + + .. versionchanged:: 21.3 + As per Bot API 7.3, most of the arguments and attributes of this class have now moved to + :class:`telegram.ChatFullInfo`. + + Args: + id (:obj:`int`): Unique identifier for this chat. + type (:obj:`str`): Type of chat, can be either :attr:`PRIVATE`, :attr:`GROUP`, + :attr:`SUPERGROUP` or :attr:`CHANNEL`. + title (:obj:`str`, optional): Title, for supergroups, channels and group chats. + username (:obj:`str`, optional): Username, for private chats, supergroups and channels if + available. + first_name (:obj:`str`, optional): First name of the other party in a private chat. + last_name (:obj:`str`, optional): Last name of the other party in a private chat. + is_forum (:obj:`bool`, optional): :obj:`True`, if the supergroup chat is a forum + (has topics_ enabled). + + .. versionadded:: 20.0 + + Attributes: + id (:obj:`int`): Unique identifier for this chat. + type (:obj:`str`): Type of chat, can be either :attr:`PRIVATE`, :attr:`GROUP`, + :attr:`SUPERGROUP` or :attr:`CHANNEL`. + title (:obj:`str`): Optional. Title, for supergroups, channels and group chats. + username (:obj:`str`): Optional. Username, for private chats, supergroups and channels if + available. + first_name (:obj:`str`): Optional. First name of the other party in a private chat. + last_name (:obj:`str`): Optional. Last name of the other party in a private chat. + is_forum (:obj:`bool`): Optional. :obj:`True`, if the supergroup chat is a forum + (has topics_ enabled). + + .. versionadded:: 20.0 + + .. _topics: https://telegram.org/blog/topics-in-groups-collectible-usernames#topics-in-groups + """ + + __slots__ = () diff --git a/_chatadministratorrights.py b/_chatadministratorrights.py new file mode 100644 index 0000000000000000000000000000000000000000..f0d0b033f62ff643bf5394879daf11519563748a --- /dev/null +++ b/_chatadministratorrights.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the class which represents a Telegram ChatAdministratorRights.""" +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class ChatAdministratorRights(TelegramObject): + """Represents the rights of an administrator in a chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`is_anonymous`, :attr:`can_manage_chat`, + :attr:`can_delete_messages`, :attr:`can_manage_video_chats`, :attr:`can_restrict_members`, + :attr:`can_promote_members`, :attr:`can_change_info`, :attr:`can_invite_users`, + :attr:`can_post_messages`, :attr:`can_edit_messages`, :attr:`can_pin_messages`, + :attr:`can_manage_topics`, :attr:`can_post_stories`, :attr:`can_delete_stories`, and + :attr:`can_edit_stories` are equal. + + .. versionadded:: 20.0 + + .. versionchanged:: 20.0 + :attr:`can_manage_topics` is considered as well when comparing objects of + this type in terms of equality. + + .. versionchanged:: 20.6 + :attr:`can_post_stories`, :attr:`can_edit_stories`, and :attr:`can_delete_stories` are + considered as well when comparing objects of this type in terms of equality. + + .. versionchanged:: 21.1 + As of this version, :attr:`can_post_stories`, :attr:`can_edit_stories`, + and :attr:`can_delete_stories` is now required. Thus, the order of arguments had to be + changed. + + Args: + is_anonymous (:obj:`bool`): :obj:`True`, if the user's presence in the chat is hidden. + can_manage_chat (:obj:`bool`): :obj:`True`, if the administrator can access the chat event + log, get boost list, see hidden supergroup and channel members, report spam messages + and ignore slow mode. Implied by any other administrator privilege. + can_delete_messages (:obj:`bool`): :obj:`True`, if the administrator can delete messages of + other users. + can_manage_video_chats (:obj:`bool`): :obj:`True`, if the administrator can manage video + chats. + can_restrict_members (:obj:`bool`): :obj:`True`, if the administrator can restrict, ban or + unban chat members, or access supergroup statistics. + can_promote_members (:obj:`bool`): :obj:`True`, if the administrator can add new + administrators with a subset of their own privileges or demote administrators + that they have promoted, directly or indirectly (promoted by administrators that + were appointed by the user). + can_change_info (:obj:`bool`): :obj:`True`, if the user is allowed to change the chat title + , photo and other settings. + can_invite_users (:obj:`bool`): :obj:`True`, if the user is allowed to invite new users to + the chat. + can_post_messages (:obj:`bool`, optional): :obj:`True`, if the administrator can post + messages in the channel, or access channel statistics; for channels only. + can_edit_messages (:obj:`bool`, optional): :obj:`True`, if the administrator can edit + messages of other users and can pin messages; for channels only. + can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to pin + messages; for groups and supergroups only. + can_post_stories (:obj:`bool`): :obj:`True`, if the administrator can post + stories to the chat. + + .. versionadded:: 20.6 + .. versionchanged:: 21.0 + |non_optional_story_argument| + can_edit_stories (:obj:`bool`): :obj:`True`, if the administrator can edit stories posted + by other users, post stories to the chat page, pin chat stories, and access the chat's + story archive + + .. versionadded:: 20.6 + .. versionchanged:: 21.0 + |non_optional_story_argument| + can_delete_stories (:obj:`bool`): :obj:`True`, if the administrator can delete + stories posted by other users. + + .. versionadded:: 20.6 + .. versionchanged:: 21.0 + |non_optional_story_argument| + can_manage_topics (:obj:`bool`, optional): :obj:`True`, if the user is allowed + to create, rename, close, and reopen forum topics; for supergroups only. + + .. versionadded:: 20.0 + + Attributes: + is_anonymous (:obj:`bool`): :obj:`True`, if the user's presence in the chat is hidden. + can_manage_chat (:obj:`bool`): :obj:`True`, if the administrator can access the chat event + log, get boost list, see hidden supergroup and channel members, report spam messages + and ignore slow mode. Implied by any other administrator privilege. + can_delete_messages (:obj:`bool`): :obj:`True`, if the administrator can delete messages of + other users. + can_manage_video_chats (:obj:`bool`): :obj:`True`, if the administrator can manage video + chats. + can_restrict_members (:obj:`bool`): :obj:`True`, if the administrator can restrict, ban or + unban chat members, or access supergroup statistics. + can_promote_members (:obj:`bool`): :obj:`True`, if the administrator can add new + administrators with a subset of their own privileges or demote administrators that he + has promoted, directly or indirectly (promoted by administrators that were appointed by + the user.) + can_change_info (:obj:`bool`): :obj:`True`, if the user is allowed to change the chat title + ,photo and other settings. + can_invite_users (:obj:`bool`): :obj:`True`, if the user is allowed to invite new users to + the chat. + can_post_messages (:obj:`bool`): Optional. :obj:`True`, if the administrator can post + messages in the channel, or access channel statistics; for channels only. + can_edit_messages (:obj:`bool`): Optional. :obj:`True`, if the administrator can edit + messages of other users and can pin messages; for channels only. + can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to pin + messages; for groups and supergroups only. + can_post_stories (:obj:`bool`): :obj:`True`, if the administrator can post + stories to the chat. + + .. versionadded:: 20.6 + .. versionchanged:: 21.0 + |non_optional_story_argument| + can_edit_stories (:obj:`bool`): :obj:`True`, if the administrator can edit stories posted + by other users, post stories to the chat page, pin chat stories, and access the chat's + story archive + + .. versionadded:: 20.6 + .. versionchanged:: 21.0 + |non_optional_story_argument| + can_delete_stories (:obj:`bool`): :obj:`True`, if the administrator can delete + stories posted by other users. + + .. versionadded:: 20.6 + .. versionchanged:: 21.0 + |non_optional_story_argument| + can_manage_topics (:obj:`bool`): Optional. :obj:`True`, if the user is allowed + to create, rename, close, and reopen forum topics; for supergroups only. + + .. versionadded:: 20.0 + """ + + __slots__ = ( + "can_change_info", + "can_delete_messages", + "can_delete_stories", + "can_edit_messages", + "can_edit_stories", + "can_invite_users", + "can_manage_chat", + "can_manage_topics", + "can_manage_video_chats", + "can_pin_messages", + "can_post_messages", + "can_post_stories", + "can_promote_members", + "can_restrict_members", + "is_anonymous", + ) + + def __init__( + self, + is_anonymous: bool, + can_manage_chat: bool, + can_delete_messages: bool, + can_manage_video_chats: bool, + can_restrict_members: bool, + can_promote_members: bool, + can_change_info: bool, + can_invite_users: bool, + can_post_stories: bool, + can_edit_stories: bool, + can_delete_stories: bool, + can_post_messages: Optional[bool] = None, + can_edit_messages: Optional[bool] = None, + can_pin_messages: Optional[bool] = None, + can_manage_topics: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ) -> None: + super().__init__(api_kwargs=api_kwargs) + # Required + self.is_anonymous: bool = is_anonymous + self.can_manage_chat: bool = can_manage_chat + self.can_delete_messages: bool = can_delete_messages + self.can_manage_video_chats: bool = can_manage_video_chats + self.can_restrict_members: bool = can_restrict_members + self.can_promote_members: bool = can_promote_members + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_post_stories: bool = can_post_stories + self.can_edit_stories: bool = can_edit_stories + self.can_delete_stories: bool = can_delete_stories + # Optionals + self.can_post_messages: Optional[bool] = can_post_messages + self.can_edit_messages: Optional[bool] = can_edit_messages + self.can_pin_messages: Optional[bool] = can_pin_messages + self.can_manage_topics: Optional[bool] = can_manage_topics + + self._id_attrs = ( + self.is_anonymous, + self.can_manage_chat, + self.can_delete_messages, + self.can_manage_video_chats, + self.can_restrict_members, + self.can_promote_members, + self.can_change_info, + self.can_invite_users, + self.can_post_messages, + self.can_edit_messages, + self.can_pin_messages, + self.can_manage_topics, + self.can_post_stories, + self.can_edit_stories, + self.can_delete_stories, + ) + + self._freeze() + + @classmethod + def all_rights(cls) -> "ChatAdministratorRights": + """ + This method returns the :class:`ChatAdministratorRights` object with all attributes set to + :obj:`True`. This is e.g. useful when changing the bot's default administrator rights with + :meth:`telegram.Bot.set_my_default_administrator_rights`. + + .. versionadded:: 20.0 + """ + return cls(*(True,) * len(cls.__slots__)) + + @classmethod + def no_rights(cls) -> "ChatAdministratorRights": + """ + This method returns the :class:`ChatAdministratorRights` object with all attributes set to + :obj:`False`. + + .. versionadded:: 20.0 + """ + return cls(*(False,) * len(cls.__slots__)) diff --git a/_chatbackground.py b/_chatbackground.py new file mode 100644 index 0000000000000000000000000000000000000000..b33fd4d91aeffeaf43464dfa0da9e177bc631376 --- /dev/null +++ b/_chatbackground.py @@ -0,0 +1,546 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains objects related to chat backgrounds.""" +from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type + +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.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class BackgroundFill(TelegramObject): + """Base class for Telegram BackgroundFill Objects. It can be one of: + + * :class:`telegram.BackgroundFillSolid` + * :class:`telegram.BackgroundFillGradient` + * :class:`telegram.BackgroundFillFreeformGradient` + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type` is equal. + + .. versionadded:: 21.2 + + Args: + type (:obj:`str`): Type of the background fill. Can be one of: + :attr:`~telegram.BackgroundFill.SOLID`, :attr:`~telegram.BackgroundFill.GRADIENT` + or :attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`. + + Attributes: + type (:obj:`str`): Type of the background fill. Can be one of: + :attr:`~telegram.BackgroundFill.SOLID`, :attr:`~telegram.BackgroundFill.GRADIENT` + or :attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`. + """ + + __slots__ = ("type",) + + SOLID: Final[constants.BackgroundFillType] = constants.BackgroundFillType.SOLID + """:const:`telegram.constants.BackgroundFillType.SOLID`""" + GRADIENT: Final[constants.BackgroundFillType] = constants.BackgroundFillType.GRADIENT + """:const:`telegram.constants.BackgroundFillType.GRADIENT`""" + FREEFORM_GRADIENT: Final[constants.BackgroundFillType] = ( + constants.BackgroundFillType.FREEFORM_GRADIENT + ) + """:const:`telegram.constants.BackgroundFillType.FREEFORM_GRADIENT`""" + + def __init__( + self, + type: str, # pylint: disable=redefined-builtin + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required by all subclasses + self.type: str = enum.get_member(constants.BackgroundFillType, type, type) + + self._id_attrs = (self.type,) + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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, + cls.FREEFORM_GRADIENT: BackgroundFillFreeformGradient, + } + + if cls is BackgroundFill and data.get("type") in _class_mapping: + return _class_mapping[data.pop("type")].de_json(data=data, bot=bot) + + return super().de_json(data=data, bot=bot) + + +class BackgroundFillSolid(BackgroundFill): + """ + The background is filled using the selected color. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`color` is equal. + + .. versionadded:: 21.2 + + Args: + color (:obj:`int`): The color of the background fill in the `RGB24` format. + + Attributes: + type (:obj:`str`): Type of the background fill. Always + :attr:`~telegram.BackgroundFill.SOLID`. + color (:obj:`int`): The color of the background fill in the `RGB24` format. + """ + + __slots__ = ("color",) + + def __init__( + self, + color: int, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.SOLID, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.color: int = color + + self._id_attrs = (self.color,) + + +class BackgroundFillGradient(BackgroundFill): + """ + The background is a gradient fill. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`top_color`, :attr:`bottom_color` + and :attr:`rotation_angle` are equal. + + .. versionadded:: 21.2 + + Args: + top_color (:obj:`int`): Top color of the gradient in the `RGB24` format. + bottom_color (:obj:`int`): Bottom color of the gradient in the `RGB24` format. + rotation_angle (:obj:`int`): Clockwise rotation angle of the background + fill in degrees; + 0-:tg-const:`telegram.constants.BackgroundFillLimit.MAX_ROTATION_ANGLE`. + + + Attributes: + type (:obj:`str`): Type of the background fill. Always + :attr:`~telegram.BackgroundFill.GRADIENT`. + top_color (:obj:`int`): Top color of the gradient in the `RGB24` format. + bottom_color (:obj:`int`): Bottom color of the gradient in the `RGB24` format. + rotation_angle (:obj:`int`): Clockwise rotation angle of the background + fill in degrees; + 0-:tg-const:`telegram.constants.BackgroundFillLimit.MAX_ROTATION_ANGLE`. + """ + + __slots__ = ("bottom_color", "rotation_angle", "top_color") + + def __init__( + self, + top_color: int, + bottom_color: int, + rotation_angle: int, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.GRADIENT, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.top_color: int = top_color + self.bottom_color: int = bottom_color + self.rotation_angle: int = rotation_angle + + self._id_attrs = (self.top_color, self.bottom_color, self.rotation_angle) + + +class BackgroundFillFreeformGradient(BackgroundFill): + """ + The background is a freeform gradient that rotates after every message in the chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`colors` is equal. + + .. versionadded:: 21.2 + + Args: + colors (Sequence[:obj:`int`]): A list of the 3 or 4 base colors that are used to + generate the freeform gradient in the `RGB24` format + + Attributes: + type (:obj:`str`): Type of the background fill. Always + :attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`. + colors (Sequence[:obj:`int`]): A list of the 3 or 4 base colors that are used to + generate the freeform gradient in the `RGB24` format + """ + + __slots__ = ("colors",) + + def __init__( + self, + colors: Sequence[int], + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.FREEFORM_GRADIENT, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.colors: Tuple[int, ...] = parse_sequence_arg(colors) + + self._id_attrs = (self.colors,) + + +class BackgroundType(TelegramObject): + """Base class for Telegram BackgroundType Objects. It can be one of: + + * :class:`telegram.BackgroundTypeFill` + * :class:`telegram.BackgroundTypeWallpaper` + * :class:`telegram.BackgroundTypePattern` + * :class:`telegram.BackgroundTypeChatTheme`. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type` is equal. + + .. versionadded:: 21.2 + + Args: + type (:obj:`str`): Type of the background. Can be one of: + :attr:`~telegram.BackgroundType.FILL`, :attr:`~telegram.BackgroundType.WALLPAPER` + :attr:`~telegram.BackgroundType.PATTERN` or + :attr:`~telegram.BackgroundType.CHAT_THEME`. + + Attributes: + type (:obj:`str`): Type of the background. Can be one of: + :attr:`~telegram.BackgroundType.FILL`, :attr:`~telegram.BackgroundType.WALLPAPER` + :attr:`~telegram.BackgroundType.PATTERN` or + :attr:`~telegram.BackgroundType.CHAT_THEME`. + + """ + + __slots__ = ("type",) + + FILL: Final[constants.BackgroundTypeType] = constants.BackgroundTypeType.FILL + """:const:`telegram.constants.BackgroundTypeType.FILL`""" + WALLPAPER: Final[constants.BackgroundTypeType] = constants.BackgroundTypeType.WALLPAPER + """:const:`telegram.constants.BackgroundTypeType.WALLPAPER`""" + PATTERN: Final[constants.BackgroundTypeType] = constants.BackgroundTypeType.PATTERN + """:const:`telegram.constants.BackgroundTypeType.PATTERN`""" + CHAT_THEME: Final[constants.BackgroundTypeType] = constants.BackgroundTypeType.CHAT_THEME + """:const:`telegram.constants.BackgroundTypeType.CHAT_THEME`""" + + def __init__( + self, + type: str, # pylint: disable=redefined-builtin + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required by all subclasses + self.type: str = enum.get_member(constants.BackgroundTypeType, type, type) + + self._id_attrs = (self.type,) + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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, + cls.PATTERN: BackgroundTypePattern, + cls.CHAT_THEME: BackgroundTypeChatTheme, + } + + if cls is BackgroundType and data.get("type") in _class_mapping: + 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) + + if "document" in data: + data["document"] = Document.de_json(data.get("document"), bot) + + return super().de_json(data=data, bot=bot) + + +class BackgroundTypeFill(BackgroundType): + """ + The background is automatically filled based on the selected colors. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`fill` and :attr:`dark_theme_dimming` are equal. + + .. versionadded:: 21.2 + + Args: + fill (:class:`telegram.BackgroundFill`): The background fill. + dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a + percentage; + 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. + + Attributes: + type (:obj:`str`): Type of the background. Always + :attr:`~telegram.BackgroundType.FILL`. + fill (:class:`telegram.BackgroundFill`): The background fill. + dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a + percentage; + 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. + """ + + __slots__ = ("dark_theme_dimming", "fill") + + def __init__( + self, + fill: BackgroundFill, + dark_theme_dimming: int, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.FILL, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.fill: BackgroundFill = fill + self.dark_theme_dimming: int = dark_theme_dimming + + self._id_attrs = (self.fill, self.dark_theme_dimming) + + +class BackgroundTypeWallpaper(BackgroundType): + """ + The background is a wallpaper in the `JPEG` format. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`document` and :attr:`dark_theme_dimming` are equal. + + .. versionadded:: 21.2 + + Args: + document (:class:`telegram.Document`): Document with the wallpaper + dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a + percentage; + 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. + is_blurred (:obj:`bool`, optional): :obj:`True`, if the wallpaper is downscaled to fit + in a 450x450 square and then box-blurred with radius 12 + is_moving (:obj:`bool`, optional): :obj:`True`, if the background moves slightly + when the device is tilted + + Attributes: + type (:obj:`str`): Type of the background. Always + :attr:`~telegram.BackgroundType.WALLPAPER`. + document (:class:`telegram.Document`): Document with the wallpaper + dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a + percentage; + 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. + is_blurred (:obj:`bool`): Optional. :obj:`True`, if the wallpaper is downscaled to fit + in a 450x450 square and then box-blurred with radius 12 + is_moving (:obj:`bool`): Optional. :obj:`True`, if the background moves slightly + when the device is tilted + """ + + __slots__ = ("dark_theme_dimming", "document", "is_blurred", "is_moving") + + def __init__( + self, + document: Document, + dark_theme_dimming: int, + is_blurred: Optional[bool] = None, + is_moving: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.WALLPAPER, api_kwargs=api_kwargs) + + with self._unfrozen(): + # Required + self.document: Document = document + self.dark_theme_dimming: int = dark_theme_dimming + # Optionals + self.is_blurred: Optional[bool] = is_blurred + self.is_moving: Optional[bool] = is_moving + + self._id_attrs = (self.document, self.dark_theme_dimming) + + +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 + chosen by the user. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`document` and :attr:`fill` and :attr:`intensity` are equal. + + .. versionadded:: 21.2 + + Args: + document (:class:`telegram.Document`): Document with the pattern. + fill (:class:`telegram.BackgroundFill`): The background fill that is combined with + the pattern. + intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled + background; + 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_INTENSITY`. + is_inverted (:obj:`int`, optional): :obj:`True`, if the background fill must be applied + only to the pattern itself. All other pixels are black in this case. For dark + themes only. + is_moving (:obj:`bool`, optional): :obj:`True`, if the background moves slightly + when the device is tilted. + + Attributes: + type (:obj:`str`): Type of the background. Always + :attr:`~telegram.BackgroundType.PATTERN`. + document (:class:`telegram.Document`): Document with the pattern. + fill (:class:`telegram.BackgroundFill`): The background fill that is combined with + the pattern. + intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled + background; + 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_INTENSITY`. + is_inverted (:obj:`int`): Optional. :obj:`True`, if the background fill must be applied + only to the pattern itself. All other pixels are black in this case. For dark + themes only. + is_moving (:obj:`bool`): Optional. :obj:`True`, if the background moves slightly + when the device is tilted. + """ + + __slots__ = ( + "document", + "fill", + "intensity", + "is_inverted", + "is_moving", + ) + + def __init__( + self, + document: Document, + fill: BackgroundFill, + intensity: int, + is_inverted: Optional[bool] = None, + is_moving: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.PATTERN, api_kwargs=api_kwargs) + + with self._unfrozen(): + # Required + self.document: Document = document + self.fill: BackgroundFill = fill + self.intensity: int = intensity + # Optionals + self.is_inverted: Optional[bool] = is_inverted + self.is_moving: Optional[bool] = is_moving + + self._id_attrs = (self.document, self.fill, self.intensity) + + +class BackgroundTypeChatTheme(BackgroundType): + """ + The background is taken directly from a built-in chat theme. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`theme_name` is equal. + + .. versionadded:: 21.2 + + Args: + theme_name (:obj:`str`): Name of the chat theme, which is usually an emoji. + + Attributes: + type (:obj:`str`): Type of the background. Always + :attr:`~telegram.BackgroundType.CHAT_THEME`. + theme_name (:obj:`str`): Name of the chat theme, which is usually an emoji. + """ + + __slots__ = ("theme_name",) + + def __init__( + self, + theme_name: str, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.CHAT_THEME, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.theme_name: str = theme_name + + self._id_attrs = (self.theme_name,) + + +class ChatBackground(TelegramObject): + """ + This object represents a chat background. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type` is equal. + + .. versionadded:: 21.2 + + Args: + type (:class:`telegram.BackgroundType`): Type of the background. + + Attributes: + type (:class:`telegram.BackgroundType`): Type of the background. + """ + + __slots__ = ("type",) + + def __init__( + self, + type: BackgroundType, # pylint: disable=redefined-builtin + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.type: BackgroundType = type + + self._id_attrs = (self.type,) + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) diff --git a/_chatboost.py b/_chatboost.py new file mode 100644 index 0000000000000000000000000000000000000000..7b972eec6d8461d57ea2c797e0e50228990a5cc6 --- /dev/null +++ b/_chatboost.py @@ -0,0 +1,451 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram ChatBoosts.""" + +from datetime import datetime +from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type + +from telegram import constants +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.datetime import extract_tzinfo_from_defaults, from_timestamp +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class ChatBoostAdded(TelegramObject): + """ + This object represents a service message about a user boosting a chat. + + Objects of this class are comparable in terms of equality. + Two objects of this class are considered equal, if their + :attr:`boost_count` are equal. + + .. versionadded:: 21.0 + + Args: + boost_count (:obj:`int`): Number of boosts added by the user. + + Attributes: + boost_count (:obj:`int`): Number of boosts added by the user. + + """ + + __slots__ = ("boost_count",) + + def __init__( + self, + boost_count: int, + *, + api_kwargs: Optional[JSONDict] = None, + ) -> None: + super().__init__(api_kwargs=api_kwargs) + self.boost_count: int = boost_count + self._id_attrs = (self.boost_count,) + + self._freeze() + + +class ChatBoostSource(TelegramObject): + """ + Base class for Telegram ChatBoostSource objects. It can be one of: + + * :class:`telegram.ChatBoostSourcePremium` + * :class:`telegram.ChatBoostSourceGiftCode` + * :class:`telegram.ChatBoostSourceGiveaway` + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`source` is equal. + + .. versionadded:: 20.8 + + Args: + source (:obj:`str`): The source of the chat boost. Can be one of: + :attr:`~telegram.ChatBoostSource.PREMIUM`, :attr:`~telegram.ChatBoostSource.GIFT_CODE`, + or :attr:`~telegram.ChatBoostSource.GIVEAWAY`. + + Attributes: + source (:obj:`str`): The source of the chat boost. Can be one of: + :attr:`~telegram.ChatBoostSource.PREMIUM`, :attr:`~telegram.ChatBoostSource.GIFT_CODE`, + or :attr:`~telegram.ChatBoostSource.GIVEAWAY`. + """ + + __slots__ = ("source",) + + PREMIUM: Final[str] = constants.ChatBoostSources.PREMIUM + """:const:`telegram.constants.ChatBoostSources.PREMIUM`""" + GIFT_CODE: Final[str] = constants.ChatBoostSources.GIFT_CODE + """:const:`telegram.constants.ChatBoostSources.GIFT_CODE`""" + GIVEAWAY: Final[str] = constants.ChatBoostSources.GIVEAWAY + """:const:`telegram.constants.ChatBoostSources.GIVEAWAY`""" + + def __init__(self, source: str, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + + # Required by all subclasses: + self.source: str = enum.get_member(constants.ChatBoostSources, source, source) + + self._id_attrs = (self.source,) + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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, + cls.GIVEAWAY: ChatBoostSourceGiveaway, + } + + if cls is ChatBoostSource and data.get("source") in _class_mapping: + 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) + + return super().de_json(data=data, bot=bot) + + +class ChatBoostSourcePremium(ChatBoostSource): + """ + The boost was obtained by subscribing to Telegram Premium or by gifting a Telegram Premium + subscription to another user. + + .. versionadded:: 20.8 + + Args: + user (:class:`telegram.User`): User that boosted the chat. + + Attributes: + source (:obj:`str`): The source of the chat boost. Always + :attr:`~telegram.ChatBoostSource.PREMIUM`. + user (:class:`telegram.User`): User that boosted the chat. + """ + + __slots__ = ("user",) + + def __init__(self, user: User, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(source=self.PREMIUM, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.user: User = user + + +class ChatBoostSourceGiftCode(ChatBoostSource): + """ + The boost was obtained by the creation of Telegram Premium gift codes to boost a chat. Each + such code boosts the chat 4 times for the duration of the corresponding Telegram Premium + subscription. + + .. versionadded:: 20.8 + + Args: + user (:class:`telegram.User`): User for which the gift code was created. + + Attributes: + source (:obj:`str`): The source of the chat boost. Always + :attr:`~telegram.ChatBoostSource.GIFT_CODE`. + user (:class:`telegram.User`): User for which the gift code was created. + """ + + __slots__ = ("user",) + + def __init__(self, user: User, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(source=self.GIFT_CODE, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.user: User = user + + +class ChatBoostSourceGiveaway(ChatBoostSource): + """ + The boost was obtained by the creation of a Telegram Premium giveaway. This boosts the chat 4 + times for the duration of the corresponding Telegram Premium subscription. + + .. versionadded:: 20.8 + + Args: + giveaway_message_id (:obj:`int`): Identifier of a message in the chat with the giveaway; + the message could have been deleted already. May be 0 if the message isn't sent yet. + user (:class:`telegram.User`, optional): User that won the prize in the giveaway if any. + is_unclaimed (:obj:`bool`, optional): :obj:`True`, if the giveaway was completed, but + there was no user to win the prize. + + Attributes: + source (:obj:`str`): Source of the boost. Always + :attr:`~telegram.ChatBoostSource.GIVEAWAY`. + giveaway_message_id (:obj:`int`): Identifier of a message in the chat with the giveaway; + the message could have been deleted already. May be 0 if the message isn't sent yet. + user (:class:`telegram.User`): Optional. User that won the prize in the giveaway if any. + is_unclaimed (:obj:`bool`): Optional. :obj:`True`, if the giveaway was completed, but + there was no user to win the prize. + """ + + __slots__ = ("giveaway_message_id", "is_unclaimed", "user") + + def __init__( + self, + giveaway_message_id: int, + user: Optional[User] = None, + is_unclaimed: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(source=self.GIVEAWAY, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.giveaway_message_id: int = giveaway_message_id + self.user: Optional[User] = user + self.is_unclaimed: Optional[bool] = is_unclaimed + + +class ChatBoost(TelegramObject): + """ + This object contains information about a chat boost. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`boost_id`, :attr:`add_date`, :attr:`expiration_date`, + and :attr:`source` are equal. + + .. versionadded:: 20.8 + + Args: + boost_id (:obj:`str`): Unique identifier of the boost. + add_date (:obj:`datetime.datetime`): Point in time when the chat was boosted. + expiration_date (:obj:`datetime.datetime`): Point in time when the boost + will automatically expire, unless the booster's Telegram Premium subscription is + prolonged. + source (:class:`telegram.ChatBoostSource`): Source of the added boost. + + Attributes: + boost_id (:obj:`str`): Unique identifier of the boost. + add_date (:obj:`datetime.datetime`): Point in time when the chat was boosted. + |datetime_localization| + expiration_date (:obj:`datetime.datetime`): Point in time when the boost + will automatically expire, unless the booster's Telegram Premium subscription is + prolonged. |datetime_localization| + source (:class:`telegram.ChatBoostSource`): Source of the added boost. + """ + + __slots__ = ("add_date", "boost_id", "expiration_date", "source") + + def __init__( + self, + boost_id: str, + add_date: datetime, + expiration_date: datetime, + source: ChatBoostSource, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + self.boost_id: str = boost_id + self.add_date: datetime = add_date + self.expiration_date: datetime = expiration_date + self.source: ChatBoostSource = source + + self._id_attrs = (self.boost_id, self.add_date, self.expiration_date, self.source) + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + 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) + + return super().de_json(data=data, bot=bot) + + +class ChatBoostUpdated(TelegramObject): + """This object represents a boost added to a chat or changed. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`chat`, and :attr:`boost` are equal. + + .. versionadded:: 20.8 + + Args: + chat (:class:`telegram.Chat`): Chat which was boosted. + boost (:class:`telegram.ChatBoost`): Information about the chat boost. + + Attributes: + chat (:class:`telegram.Chat`): Chat which was boosted. + boost (:class:`telegram.ChatBoost`): Information about the chat boost. + """ + + __slots__ = ("boost", "chat") + + def __init__( + self, + chat: Chat, + boost: ChatBoost, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + self.chat: Chat = chat + self.boost: ChatBoost = boost + + self._id_attrs = (self.chat.id, self.boost) + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) + + +class ChatBoostRemoved(TelegramObject): + """ + This object represents a boost removed from a chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`chat`, :attr:`boost_id`, :attr:`remove_date`, and + :attr:`source` are equal. + + Args: + chat (:class:`telegram.Chat`): Chat which was boosted. + boost_id (:obj:`str`): Unique identifier of the boost. + remove_date (:obj:`datetime.datetime`): Point in time when the boost was removed. + source (:class:`telegram.ChatBoostSource`): Source of the removed boost. + + Attributes: + chat (:class:`telegram.Chat`): Chat which was boosted. + boost_id (:obj:`str`): Unique identifier of the boost. + remove_date (:obj:`datetime.datetime`): Point in time when the boost was removed. + |datetime_localization| + source (:class:`telegram.ChatBoostSource`): Source of the removed boost. + """ + + __slots__ = ("boost_id", "chat", "remove_date", "source") + + def __init__( + self, + chat: Chat, + boost_id: str, + remove_date: datetime, + source: ChatBoostSource, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + self.chat: Chat = chat + self.boost_id: str = boost_id + self.remove_date: datetime = remove_date + self.source: ChatBoostSource = source + + self._id_attrs = (self.chat, self.boost_id, self.remove_date, self.source) + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + loc_tzinfo = extract_tzinfo_from_defaults(bot) + data["remove_date"] = from_timestamp(data["remove_date"], tzinfo=loc_tzinfo) + + return super().de_json(data=data, bot=bot) + + +class UserChatBoosts(TelegramObject): + """This object represents a list of boosts added to a chat by a user. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`boosts` are equal. + + .. versionadded:: 20.8 + + Args: + boosts (Sequence[:class:`telegram.ChatBoost`]): List of boosts added to the chat by the + user. + + Attributes: + boosts (Tuple[:class:`telegram.ChatBoost`]): List of boosts added to the chat by the user. + """ + + __slots__ = ("boosts",) + + def __init__( + self, + boosts: Sequence[ChatBoost], + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + self.boosts: Tuple[ChatBoost, ...] = parse_sequence_arg(boosts) + + self._id_attrs = (self.boosts,) + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) diff --git a/_chatfullinfo.py b/_chatfullinfo.py new file mode 100644 index 0000000000000000000000000000000000000000..de26101f33c4f8d4e46c78c4acdad1d6114e581c --- /dev/null +++ b/_chatfullinfo.py @@ -0,0 +1,549 @@ +#!/usr/bin/env python +# pylint: disable=redefined-builtin +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram ChatFullInfo.""" +from datetime import datetime +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._birthdate import Birthdate +from telegram._chat import Chat, _ChatBase +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.datetime import extract_tzinfo_from_defaults, from_timestamp +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot, BusinessIntro, BusinessLocation, BusinessOpeningHours, Message + + +class ChatFullInfo(_ChatBase): + """ + This object contains full information about a chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`~telegram.Chat.id` is equal. + + .. versionadded:: 21.2 + + .. versionchanged:: 21.3 + Explicit support for all shortcut methods known from :class:`telegram.Chat` on this + object. Previously those were only available because this class inherited from + :class:`telegram.Chat`. + + Args: + id (:obj:`int`): Unique identifier for this chat. + type (:obj:`str`): Type of chat, can be either :attr:`PRIVATE`, :attr:`GROUP`, + :attr:`SUPERGROUP` or :attr:`CHANNEL`. + accent_color_id (:obj:`int`, optional): Identifier of the + :class:`accent color ` for the chat name and + backgrounds of the chat photo, reply header, and link preview. See `accent colors`_ + for more details. + + .. versionadded:: 20.8 + max_reaction_count (:obj:`int`): The maximum number of reactions that can be set on a + message in the chat. + + .. versionadded:: 21.2 + title (:obj:`str`, optional): Title, for supergroups, channels and group chats. + username (:obj:`str`, optional): Username, for private chats, supergroups and channels if + available. + first_name (:obj:`str`, optional): First name of the other party in a private chat. + last_name (:obj:`str`, optional): Last name of the other party in a private chat. + is_forum (:obj:`bool`, optional): :obj:`True`, if the supergroup chat is a forum + (has topics_ enabled). + + .. versionadded:: 20.0 + photo (:class:`telegram.ChatPhoto`, optional): Chat photo. + active_usernames (Sequence[:obj:`str`], optional): If set, the list of all `active chat + usernames `_; for private chats, supergroups and channels. + + .. versionadded:: 20.0 + birthdate (:class:`telegram.Birthdate`, optional): For private chats, + the date of birth of the user. + + .. versionadded:: 21.1 + business_intro (:class:`telegram.BusinessIntro`, optional): For private chats with + business accounts, the intro of the business. + + .. versionadded:: 21.1 + business_location (:class:`telegram.BusinessLocation`, optional): For private chats with + business accounts, the location of the business. + + .. versionadded:: 21.1 + business_opening_hours (:class:`telegram.BusinessOpeningHours`, optional): For private + chats with business accounts, the opening hours of the business. + + .. versionadded:: 21.1 + personal_chat (:class:`telegram.Chat`, optional): For private chats, the personal channel + of the user. + + .. versionadded:: 21.1 + available_reactions (Sequence[:class:`telegram.ReactionType`], optional): List of available + reactions allowed in the chat. If omitted, then all of + :const:`telegram.constants.ReactionEmoji` are allowed. + + .. versionadded:: 20.8 + background_custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of emoji chosen + by the chat for the reply header and link preview background. + + .. versionadded:: 20.8 + profile_accent_color_id (:obj:`int`, optional): Identifier of the + :class:`accent color ` for the chat's profile + background. See profile `accent colors`_ for more details. + + .. versionadded:: 20.8 + profile_background_custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of + the emoji chosen by the chat for its profile background. + + .. versionadded:: 20.8 + emoji_status_custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of emoji + status of the chat or the other party in a private chat. + + .. versionadded:: 20.0 + emoji_status_expiration_date (:class:`datetime.datetime`, optional): Expiration date of + emoji status of the chat or the other party in a private chat, as a datetime object, + if any. + + |datetime_localization| + + .. versionadded:: 20.5 + bio (:obj:`str`, optional): Bio of the other party in a private chat. + has_private_forwards (:obj:`bool`, optional): :obj:`True`, if privacy settings of the other + party in the private chat allows to use ``tg://user?id=`` links only in chats + with the user. + + .. versionadded:: 13.9 + has_restricted_voice_and_video_messages (:obj:`bool`, optional): :obj:`True`, if the + privacy settings of the other party restrict sending voice and video note messages + in the private chat. + + .. versionadded:: 20.0 + join_to_send_messages (:obj:`bool`, optional): :obj:`True`, if users need to join the + supergroup before they can send messages. + + .. versionadded:: 20.0 + join_by_request (:obj:`bool`, optional): :obj:`True`, if all users directly joining the + supergroup without using an invite link need to be approved by supergroup + administrators. + + .. versionadded:: 20.0 + description (:obj:`str`, optional): Description, for groups, supergroups and channel chats. + invite_link (:obj:`str`, optional): Primary invite link, for groups, supergroups and + channel. + pinned_message (:class:`telegram.Message`, optional): The most recent pinned message + (by sending date). + permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions, + for groups and supergroups. + slow_mode_delay (:obj:`int`, optional): For supergroups, the minimum allowed delay between + consecutive messages sent by each unprivileged user. + unrestrict_boost_count (:obj:`int`, optional): For supergroups, the minimum number of + boosts that a non-administrator user needs to add in order to ignore slow mode and chat + permissions. + + .. versionadded:: 21.0 + message_auto_delete_time (:obj:`int`, optional): The time after which all messages sent to + the chat will be automatically deleted; in seconds. + + .. versionadded:: 13.4 + has_aggressive_anti_spam_enabled (:obj:`bool`, optional): :obj:`True`, if aggressive + anti-spam checks are enabled in the supergroup. The field is only available to chat + administrators. + + .. versionadded:: 20.0 + has_hidden_members (:obj:`bool`, optional): :obj:`True`, if non-administrators can only + get the list of bots and administrators in the chat. + + .. versionadded:: 20.0 + has_protected_content (:obj:`bool`, optional): :obj:`True`, if messages from the chat can't + be forwarded to other chats. + + .. versionadded:: 13.9 + has_visible_history (:obj:`bool`, optional): :obj:`True`, if new chat members will have + access to old messages; available only to chat administrators. + + .. versionadded:: 20.8 + sticker_set_name (:obj:`str`, optional): For supergroups, name of group sticker set. + can_set_sticker_set (:obj:`bool`, optional): :obj:`True`, if the bot can change group the + sticker set. + custom_emoji_sticker_set_name (:obj:`str`, optional): For supergroups, the name of the + group's custom emoji sticker set. Custom emoji from this set can be used by all users + and bots in the group. + + .. versionadded:: 21.0 + linked_chat_id (:obj:`int`, optional): Unique identifier for the linked chat, i.e. the + discussion group identifier for a channel and vice versa; for supergroups and channel + chats. + location (:class:`telegram.ChatLocation`, optional): For supergroups, the location to which + the supergroup is connected. + can_send_paid_media (:obj:`bool`, optional): :obj:`True`, if paid media messages can be + sent or forwarded to the channel chat. The field is available only for channel chats. + + .. versionadded:: 21.4 + + Attributes: + id (:obj:`int`): Unique identifier for this chat. + type (:obj:`str`): Type of chat, can be either :attr:`PRIVATE`, :attr:`GROUP`, + :attr:`SUPERGROUP` or :attr:`CHANNEL`. + accent_color_id (:obj:`int`): Optional. Identifier of the + :class:`accent color ` for the chat name and + backgrounds of the chat photo, reply header, and link preview. See `accent colors`_ + for more details. + + .. versionadded:: 20.8 + max_reaction_count (:obj:`int`): The maximum number of reactions that can be set on a + message in the chat. + + .. versionadded:: 21.2 + title (:obj:`str`, optional): Title, for supergroups, channels and group chats. + username (:obj:`str`, optional): Username, for private chats, supergroups and channels if + available. + first_name (:obj:`str`, optional): First name of the other party in a private chat. + last_name (:obj:`str`, optional): Last name of the other party in a private chat. + is_forum (:obj:`bool`, optional): :obj:`True`, if the supergroup chat is a forum + (has topics_ enabled). + + .. versionadded:: 20.0 + photo (:class:`telegram.ChatPhoto`): Optional. Chat photo. + active_usernames (Tuple[:obj:`str`]): Optional. If set, the list of all `active chat + usernames `_; for private chats, supergroups and channels. + + This list is empty if the chat has no active usernames or this chat instance was not + obtained via :meth:`~telegram.Bot.get_chat`. + + .. versionadded:: 20.0 + birthdate (:class:`telegram.Birthdate`): Optional. For private chats, + the date of birth of the user. + + .. versionadded:: 21.1 + business_intro (:class:`telegram.BusinessIntro`): Optional. For private chats with + business accounts, the intro of the business. + + .. versionadded:: 21.1 + business_location (:class:`telegram.BusinessLocation`): Optional. For private chats with + business accounts, the location of the business. + + .. versionadded:: 21.1 + business_opening_hours (:class:`telegram.BusinessOpeningHours`): Optional. For private + chats with business accounts, the opening hours of the business. + + .. versionadded:: 21.1 + personal_chat (:class:`telegram.Chat`): Optional. For private chats, the personal channel + of the user. + + .. versionadded:: 21.1 + available_reactions (Tuple[:class:`telegram.ReactionType`]): Optional. List of available + reactions allowed in the chat. If omitted, then all of + :const:`telegram.constants.ReactionEmoji` are allowed. + + .. versionadded:: 20.8 + background_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of emoji chosen + by the chat for the reply header and link preview background. + + .. versionadded:: 20.8 + profile_accent_color_id (:obj:`int`): Optional. Identifier of the + :class:`accent color ` for the chat's profile + background. See profile `accent colors`_ for more details. + + .. versionadded:: 20.8 + profile_background_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of + the emoji chosen by the chat for its profile background. + + .. versionadded:: 20.8 + emoji_status_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of emoji + status of the chat or the other party in a private chat. + + .. versionadded:: 20.0 + emoji_status_expiration_date (:class:`datetime.datetime`): Optional. Expiration date of + emoji status of the chat or the other party in a private chat, as a datetime object, + if any. + + |datetime_localization| + + .. versionadded:: 20.5 + bio (:obj:`str`): Optional. Bio of the other party in a private chat. + has_private_forwards (:obj:`bool`): Optional. :obj:`True`, if privacy settings of the other + party in the private chat allows to use ``tg://user?id=`` links only in chats + with the user. + + .. versionadded:: 13.9 + has_restricted_voice_and_video_messages (:obj:`bool`): Optional. :obj:`True`, if the + privacy settings of the other party restrict sending voice and video note messages + in the private chat. + + .. versionadded:: 20.0 + join_to_send_messages (:obj:`bool`): Optional. :obj:`True`, if users need to join + the supergroup before they can send messages. + + .. versionadded:: 20.0 + join_by_request (:obj:`bool`): Optional. :obj:`True`, if all users directly joining the + supergroup without using an invite link need to be approved by supergroup + administrators. + + .. versionadded:: 20.0 + description (:obj:`str`): Optional. Description, for groups, supergroups and channel chats. + invite_link (:obj:`str`): Optional. Primary invite link, for groups, supergroups and + channel. + pinned_message (:class:`telegram.Message`): Optional. The most recent pinned message + (by sending date). + permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions, + for groups and supergroups. + slow_mode_delay (:obj:`int`): Optional. For supergroups, the minimum allowed delay between + consecutive messages sent by each unprivileged user. + unrestrict_boost_count (:obj:`int`): Optional. For supergroups, the minimum number of + boosts that a non-administrator user needs to add in order to ignore slow mode and chat + permissions. + + .. versionadded:: 21.0 + message_auto_delete_time (:obj:`int`): Optional. The time after which all messages sent to + the chat will be automatically deleted; in seconds. + + .. versionadded:: 13.4 + has_aggressive_anti_spam_enabled (:obj:`bool`): Optional. :obj:`True`, if aggressive + anti-spam checks are enabled in the supergroup. The field is only available to chat + administrators. + + .. versionadded:: 20.0 + has_hidden_members (:obj:`bool`): Optional. :obj:`True`, if non-administrators can only + get the list of bots and administrators in the chat. + + .. versionadded:: 20.0 + has_protected_content (:obj:`bool`): Optional. :obj:`True`, if messages from the chat can't + be forwarded to other chats. + + .. versionadded:: 13.9 + has_visible_history (:obj:`bool`): Optional. :obj:`True`, if new chat members will have + access to old messages; available only to chat administrators. + + .. versionadded:: 20.8 + sticker_set_name (:obj:`str`): Optional. For supergroups, name of Group sticker set. + can_set_sticker_set (:obj:`bool`): Optional. :obj:`True`, if the bot can change group the + sticker set. + custom_emoji_sticker_set_name (:obj:`str`): Optional. For supergroups, the name of the + group's custom emoji sticker set. Custom emoji from this set can be used by all users + and bots in the group. + + .. versionadded:: 21.0 + linked_chat_id (:obj:`int`): Optional. Unique identifier for the linked chat, i.e. the + discussion group identifier for a channel and vice versa; for supergroups and channel + chats. + location (:class:`telegram.ChatLocation`): Optional. For supergroups, the location to which + the supergroup is connected. + can_send_paid_media (:obj:`bool`): Optional. :obj:`True`, if paid media messages can be + sent or forwarded to the channel chat. The field is available only for channel chats. + + .. versionadded:: 21.4 + + .. _accent colors: https://core.telegram.org/bots/api#accent-colors + .. _topics: https://telegram.org/blog/topics-in-groups-collectible-usernames#topics-in-groups + """ + + __slots__ = ( + "accent_color_id", + "active_usernames", + "available_reactions", + "background_custom_emoji_id", + "bio", + "birthdate", + "business_intro", + "business_location", + "business_opening_hours", + "can_send_paid_media", + "can_set_sticker_set", + "custom_emoji_sticker_set_name", + "description", + "emoji_status_custom_emoji_id", + "emoji_status_expiration_date", + "has_aggressive_anti_spam_enabled", + "has_hidden_members", + "has_private_forwards", + "has_protected_content", + "has_restricted_voice_and_video_messages", + "has_visible_history", + "invite_link", + "join_by_request", + "join_to_send_messages", + "linked_chat_id", + "location", + "max_reaction_count", + "message_auto_delete_time", + "permissions", + "personal_chat", + "photo", + "pinned_message", + "profile_accent_color_id", + "profile_background_custom_emoji_id", + "slow_mode_delay", + "sticker_set_name", + "unrestrict_boost_count", + ) + + def __init__( + self, + id: int, + type: str, + accent_color_id: int, + max_reaction_count: int, + title: Optional[str] = None, + username: Optional[str] = None, + first_name: Optional[str] = None, + last_name: Optional[str] = None, + is_forum: Optional[bool] = None, + photo: Optional[ChatPhoto] = None, + active_usernames: Optional[Sequence[str]] = None, + birthdate: Optional[Birthdate] = None, + business_intro: Optional["BusinessIntro"] = None, + business_location: Optional["BusinessLocation"] = None, + business_opening_hours: Optional["BusinessOpeningHours"] = None, + personal_chat: Optional["Chat"] = None, + available_reactions: Optional[Sequence[ReactionType]] = None, + background_custom_emoji_id: Optional[str] = None, + profile_accent_color_id: Optional[int] = None, + profile_background_custom_emoji_id: Optional[str] = None, + emoji_status_custom_emoji_id: Optional[str] = None, + emoji_status_expiration_date: Optional[datetime] = None, + bio: Optional[str] = None, + has_private_forwards: Optional[bool] = None, + has_restricted_voice_and_video_messages: Optional[bool] = None, + join_to_send_messages: Optional[bool] = None, + join_by_request: Optional[bool] = None, + description: Optional[str] = None, + invite_link: Optional[str] = None, + pinned_message: Optional["Message"] = None, + permissions: Optional[ChatPermissions] = None, + slow_mode_delay: Optional[int] = None, + unrestrict_boost_count: Optional[int] = None, + message_auto_delete_time: Optional[int] = None, + has_aggressive_anti_spam_enabled: Optional[bool] = None, + has_hidden_members: Optional[bool] = None, + has_protected_content: Optional[bool] = None, + has_visible_history: Optional[bool] = None, + sticker_set_name: Optional[str] = None, + can_set_sticker_set: Optional[bool] = None, + custom_emoji_sticker_set_name: Optional[str] = None, + linked_chat_id: Optional[int] = None, + location: Optional[ChatLocation] = None, + can_send_paid_media: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__( + id=id, + type=type, + title=title, + username=username, + first_name=first_name, + last_name=last_name, + is_forum=is_forum, + api_kwargs=api_kwargs, + ) + + # Required and unique to this class- + with self._unfrozen(): + self.max_reaction_count: int = max_reaction_count + self.photo: Optional[ChatPhoto] = photo + self.bio: Optional[str] = bio + self.has_private_forwards: Optional[bool] = has_private_forwards + self.description: Optional[str] = description + self.invite_link: Optional[str] = invite_link + self.pinned_message: Optional[Message] = pinned_message + self.permissions: Optional[ChatPermissions] = permissions + self.slow_mode_delay: Optional[int] = slow_mode_delay + self.message_auto_delete_time: Optional[int] = ( + int(message_auto_delete_time) if message_auto_delete_time is not None else None + ) + self.has_protected_content: Optional[bool] = has_protected_content + self.has_visible_history: Optional[bool] = has_visible_history + self.sticker_set_name: Optional[str] = sticker_set_name + self.can_set_sticker_set: Optional[bool] = can_set_sticker_set + self.linked_chat_id: Optional[int] = linked_chat_id + self.location: Optional[ChatLocation] = location + self.join_to_send_messages: Optional[bool] = join_to_send_messages + self.join_by_request: Optional[bool] = join_by_request + self.has_restricted_voice_and_video_messages: Optional[bool] = ( + has_restricted_voice_and_video_messages + ) + self.active_usernames: Tuple[str, ...] = parse_sequence_arg(active_usernames) + self.emoji_status_custom_emoji_id: Optional[str] = emoji_status_custom_emoji_id + self.emoji_status_expiration_date: Optional[datetime] = emoji_status_expiration_date + self.has_aggressive_anti_spam_enabled: Optional[bool] = ( + has_aggressive_anti_spam_enabled + ) + self.has_hidden_members: Optional[bool] = has_hidden_members + self.available_reactions: Optional[Tuple[ReactionType, ...]] = parse_sequence_arg( + available_reactions + ) + self.accent_color_id: Optional[int] = accent_color_id + self.background_custom_emoji_id: Optional[str] = background_custom_emoji_id + self.profile_accent_color_id: Optional[int] = profile_accent_color_id + self.profile_background_custom_emoji_id: Optional[str] = ( + profile_background_custom_emoji_id + ) + self.unrestrict_boost_count: Optional[int] = unrestrict_boost_count + self.custom_emoji_sticker_set_name: Optional[str] = custom_emoji_sticker_set_name + self.birthdate: Optional[Birthdate] = birthdate + self.personal_chat: Optional[Chat] = personal_chat + self.business_intro: Optional[BusinessIntro] = business_intro + 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 + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + data["emoji_status_expiration_date"] = from_timestamp( + data.get("emoji_status_expiration_date"), tzinfo=loc_tzinfo + ) + + data["photo"] = ChatPhoto.de_json(data.get("photo"), bot) + + from telegram import ( # pylint: disable=import-outside-toplevel + BusinessIntro, + BusinessLocation, + BusinessOpeningHours, + 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 + ) + + return super().de_json(data=data, bot=bot) diff --git a/_chatinvitelink.py b/_chatinvitelink.py new file mode 100644 index 0000000000000000000000000000000000000000..b26de4e332b25fa40943dcbf411bccd3b66e919c --- /dev/null +++ b/_chatinvitelink.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents an invite link for a chat.""" +import datetime +from typing import TYPE_CHECKING, Optional + +from telegram._telegramobject import TelegramObject +from telegram._user import User +from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class ChatInviteLink(TelegramObject): + """This object represents an invite link for a chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`invite_link`, :attr:`creator`, :attr:`creates_join_request`, + :attr:`is_primary` and :attr:`is_revoked` are equal. + + .. versionadded:: 13.4 + .. versionchanged:: 20.0 + + * The argument & attribute :attr:`creates_join_request` is now required to comply with the + Bot API. + * Comparing objects of this class now also takes :attr:`creates_join_request` into account. + + Args: + invite_link (:obj:`str`): The invite link. + creator (:class:`telegram.User`): Creator of the link. + creates_join_request (:obj:`bool`): :obj:`True`, if users joining the chat via + the link need to be approved by chat administrators. + + .. versionadded:: 13.8 + is_primary (:obj:`bool`): :obj:`True`, if the link is primary. + is_revoked (:obj:`bool`): :obj:`True`, if the link is revoked. + expire_date (:class:`datetime.datetime`, optional): Date when the link will expire or + has been expired. + + .. versionchanged:: 20.3 + |datetime_localization| + member_limit (:obj:`int`, optional): Maximum number of users that can be members of the + chat simultaneously after joining the chat via this invite link; + :tg-const:`telegram.constants.ChatInviteLinkLimit.MIN_MEMBER_LIMIT`- + :tg-const:`telegram.constants.ChatInviteLinkLimit.MAX_MEMBER_LIMIT`. + name (:obj:`str`, optional): Invite link name. + 0-:tg-const:`telegram.constants.ChatInviteLinkLimit.NAME_LENGTH` characters. + + .. versionadded:: 13.8 + pending_join_request_count (:obj:`int`, optional): Number of pending join requests + created using this link. + + .. versionadded:: 13.8 + subscription_period (:obj:`int`, optional): The number of seconds the subscription will be + active for before the next payment. + + .. versionadded:: 21.5 + subscription_price (:obj:`int`, optional): The amount of Telegram Stars a user must pay + initially and after each subsequent subscription period to be a member of the chat + using the link. + + .. versionadded:: 21.5 + + Attributes: + invite_link (:obj:`str`): The invite link. If the link was created by another chat + administrator, then the second part of the link will be replaced with ``'…'``. + creator (:class:`telegram.User`): Creator of the link. + creates_join_request (:obj:`bool`): :obj:`True`, if users joining the chat via + the link need to be approved by chat administrators. + + .. versionadded:: 13.8 + is_primary (:obj:`bool`): :obj:`True`, if the link is primary. + is_revoked (:obj:`bool`): :obj:`True`, if the link is revoked. + expire_date (:class:`datetime.datetime`): Optional. Date when the link will expire or + has been expired. + + .. versionchanged:: 20.3 + |datetime_localization| + member_limit (:obj:`int`): Optional. Maximum number of users that can be members + of the chat simultaneously after joining the chat via this invite link; + :tg-const:`telegram.constants.ChatInviteLinkLimit.MIN_MEMBER_LIMIT`- + :tg-const:`telegram.constants.ChatInviteLinkLimit.MAX_MEMBER_LIMIT`. + name (:obj:`str`): Optional. Invite link name. + 0-:tg-const:`telegram.constants.ChatInviteLinkLimit.NAME_LENGTH` characters. + + .. versionadded:: 13.8 + pending_join_request_count (:obj:`int`): Optional. Number of pending join requests + created using this link. + + .. versionadded:: 13.8 + subscription_period (:obj:`int`): Optional. The number of seconds the subscription will be + active for before the next payment. + + .. versionadded:: 21.5 + subscription_price (:obj:`int`): Optional. The amount of Telegram Stars a user must pay + initially and after each subsequent subscription period to be a member of the chat + using the link. + + .. versionadded:: 21.5 + + """ + + __slots__ = ( + "creates_join_request", + "creator", + "expire_date", + "invite_link", + "is_primary", + "is_revoked", + "member_limit", + "name", + "pending_join_request_count", + "subscription_period", + "subscription_price", + ) + + def __init__( + self, + invite_link: str, + creator: User, + creates_join_request: bool, + is_primary: bool, + is_revoked: bool, + expire_date: Optional[datetime.datetime] = None, + member_limit: Optional[int] = None, + name: Optional[str] = None, + pending_join_request_count: Optional[int] = None, + subscription_period: Optional[int] = None, + subscription_price: Optional[int] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.invite_link: str = invite_link + self.creator: User = creator + self.creates_join_request: bool = creates_join_request + self.is_primary: bool = is_primary + self.is_revoked: bool = is_revoked + + # Optionals + self.expire_date: Optional[datetime.datetime] = expire_date + self.member_limit: Optional[int] = member_limit + self.name: Optional[str] = name + self.pending_join_request_count: Optional[int] = ( + int(pending_join_request_count) if pending_join_request_count is not None else None + ) + self.subscription_period: Optional[int] = subscription_period + self.subscription_price: Optional[int] = subscription_price + + self._id_attrs = ( + self.invite_link, + self.creates_join_request, + self.creator, + self.is_primary, + self.is_revoked, + ) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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["expire_date"] = from_timestamp(data.get("expire_date", None), tzinfo=loc_tzinfo) + + return super().de_json(data=data, bot=bot) diff --git a/_chatjoinrequest.py b/_chatjoinrequest.py new file mode 100644 index 0000000000000000000000000000000000000000..9c444d97b4d7b843cb328cf3159acab81c5732ba --- /dev/null +++ b/_chatjoinrequest.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram ChatJoinRequest.""" +import datetime +from typing import TYPE_CHECKING, Optional + +from telegram._chat import Chat +from telegram._chatinvitelink import ChatInviteLink +from telegram._telegramobject import TelegramObject +from telegram._user import User +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 + +if TYPE_CHECKING: + from telegram import Bot + + +class ChatJoinRequest(TelegramObject): + """This object represents a join request sent to a chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`chat`, :attr:`from_user` and :attr:`date` are equal. + + Note: + * Since Bot API 5.5, bots are allowed to contact users who sent a join request to a chat + where the bot is an administrator with the + :attr:`~telegram.ChatMemberAdministrator.can_invite_users` administrator right - even + if the user never interacted with the bot before. + * Telegram does not guarantee that :attr:`from_user.id ` coincides with the + ``chat_id`` of the user. Please use :attr:`user_chat_id` to contact the user in + response to their join request. + + .. versionadded:: 13.8 + .. versionchanged:: 20.1 + In Bot API 6.5 the argument :paramref:`user_chat_id` was added, which changes the position + of the optional arguments :paramref:`bio` and :paramref:`invite_link`. + + Args: + chat (:class:`telegram.Chat`): Chat to which the request was sent. + from_user (:class:`telegram.User`): User that sent the join request. + date (:class:`datetime.datetime`): Date the request was sent. + + .. versionchanged:: 20.3 + |datetime_localization| + user_chat_id (:obj:`int`): Identifier of a private chat with the user who sent the join + request. This number may have more than 32 significant bits and some programming + languages may have difficulty/silent defects in interpreting it. But it has at most 52 + significant bits, so a 64-bit integer or double-precision float type are safe for + storing this identifier. The bot can use this identifier for 5 minutes to send messages + until the join request is processed, assuming no other administrator contacted the + user. + + .. versionadded:: 20.1 + bio (:obj:`str`, optional): Bio of the user. + invite_link (:class:`telegram.ChatInviteLink`, optional): Chat invite link that was used + by the user to send the join request. + + Attributes: + chat (:class:`telegram.Chat`): Chat to which the request was sent. + from_user (:class:`telegram.User`): User that sent the join request. + date (:class:`datetime.datetime`): Date the request was sent. + + .. versionchanged:: 20.3 + |datetime_localization| + user_chat_id (:obj:`int`): Identifier of a private chat with the user who sent the join + request. This number may have more than 32 significant bits and some programming + languages may have difficulty/silent defects in interpreting it. But it has at most 52 + significant bits, so a 64-bit integer or double-precision float type are safe for + storing this identifier. The bot can use this identifier for 24 hours to send messages + until the join request is processed, assuming no other administrator contacted the + user. + + .. versionadded:: 20.1 + bio (:obj:`str`): Optional. Bio of the user. + invite_link (:class:`telegram.ChatInviteLink`): Optional. Chat invite link that was used + by the user to send the join request. + + Note: + When a user joins a *public* group via an invite link, this attribute may not + be present. However, this behavior is undocument and may be subject to change. + See `this GitHub thread `_ + for some discussion. + + """ + + __slots__ = ("bio", "chat", "date", "from_user", "invite_link", "user_chat_id") + + def __init__( + self, + chat: Chat, + from_user: User, + date: datetime.datetime, + user_chat_id: int, + bio: Optional[str] = None, + invite_link: Optional[ChatInviteLink] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.chat: Chat = chat + self.from_user: User = from_user + self.date: datetime.datetime = date + self.user_chat_id: int = user_chat_id + + # Optionals + self.bio: Optional[str] = bio + self.invite_link: Optional[ChatInviteLink] = invite_link + + self._id_attrs = (self.chat, self.from_user, self.date) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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["date"] = from_timestamp(data.get("date", None), tzinfo=loc_tzinfo) + data["invite_link"] = ChatInviteLink.de_json(data.get("invite_link"), bot) + + return super().de_json(data=data, bot=bot) + + async def approve( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.approve_chat_join_request( + chat_id=update.effective_chat.id, user_id=update.effective_user.id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.approve_chat_join_request`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().approve_chat_join_request( + chat_id=self.chat.id, + user_id=self.from_user.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def decline( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.decline_chat_join_request( + chat_id=update.effective_chat.id, user_id=update.effective_user.id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.decline_chat_join_request`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().decline_chat_join_request( + chat_id=self.chat.id, + user_id=self.from_user.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) diff --git a/_chatlocation.py b/_chatlocation.py new file mode 100644 index 0000000000000000000000000000000000000000..04f9854a23aee0269bd482d028ab8825a359e5e6 --- /dev/null +++ b/_chatlocation.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a location to which a chat is connected.""" + +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.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class ChatLocation(TelegramObject): + """This object represents a location to which a chat is connected. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`location` is equal. + + Args: + location (:class:`telegram.Location`): The location to which the supergroup is connected. + Can't be a live location. + address (:obj:`str`): Location address; + :tg-const:`telegram.ChatLocation.MIN_ADDRESS`- + :tg-const:`telegram.ChatLocation.MAX_ADDRESS` characters, as defined by the chat owner. + Attributes: + location (:class:`telegram.Location`): The location to which the supergroup is connected. + Can't be a live location. + address (:obj:`str`): Location address; + :tg-const:`telegram.ChatLocation.MIN_ADDRESS`- + :tg-const:`telegram.ChatLocation.MAX_ADDRESS` characters, as defined by the chat owner. + + """ + + __slots__ = ("address", "location") + + def __init__( + self, + location: Location, + address: str, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.location: Location = location + self.address: str = address + + self._id_attrs = (self.location,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) + + MIN_ADDRESS: Final[int] = constants.LocationLimit.MIN_CHAT_LOCATION_ADDRESS + """:const:`telegram.constants.LocationLimit.MIN_CHAT_LOCATION_ADDRESS` + + .. versionadded:: 20.0 + """ + MAX_ADDRESS: Final[int] = constants.LocationLimit.MAX_CHAT_LOCATION_ADDRESS + """:const:`telegram.constants.LocationLimit.MAX_CHAT_LOCATION_ADDRESS` + + .. versionadded:: 20.0 + """ diff --git a/_chatmember.py b/_chatmember.py new file mode 100644 index 0000000000000000000000000000000000000000..da84516b16563960b7b8ba995eca3737fd38cc4f --- /dev/null +++ b/_chatmember.py @@ -0,0 +1,665 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram ChatMember.""" + +import datetime +from typing import TYPE_CHECKING, Dict, Final, Optional, Type + +from telegram import constants +from telegram._telegramobject import TelegramObject +from telegram._user import User +from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class ChatMember(TelegramObject): + """Base class for Telegram ChatMember Objects. + Currently, the following 6 types of chat members are supported: + + * :class:`telegram.ChatMemberOwner` + * :class:`telegram.ChatMemberAdministrator` + * :class:`telegram.ChatMemberMember` + * :class:`telegram.ChatMemberRestricted` + * :class:`telegram.ChatMemberLeft` + * :class:`telegram.ChatMemberBanned` + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`user` and :attr:`status` are equal. + + Examples: + :any:`Chat Member Bot ` + + .. versionchanged:: 20.0 + + * As of Bot API 5.3, :class:`ChatMember` is nothing but the base class for the subclasses + listed above and is no longer returned directly by :meth:`~telegram.Bot.get_chat`. + Therefore, most of the arguments and attributes were removed and you should no longer + use :class:`ChatMember` directly. + * The constant ``ChatMember.CREATOR`` was replaced by :attr:`~telegram.ChatMember.OWNER` + * The constant ``ChatMember.KICKED`` was replaced by :attr:`~telegram.ChatMember.BANNED` + + Args: + user (:class:`telegram.User`): Information about the user. + status (:obj:`str`): The member's status in the chat. Can be + :attr:`~telegram.ChatMember.ADMINISTRATOR`, :attr:`~telegram.ChatMember.OWNER`, + :attr:`~telegram.ChatMember.BANNED`, :attr:`~telegram.ChatMember.LEFT`, + :attr:`~telegram.ChatMember.MEMBER` or :attr:`~telegram.ChatMember.RESTRICTED`. + + Attributes: + user (:class:`telegram.User`): Information about the user. + status (:obj:`str`): The member's status in the chat. Can be + :attr:`~telegram.ChatMember.ADMINISTRATOR`, :attr:`~telegram.ChatMember.OWNER`, + :attr:`~telegram.ChatMember.BANNED`, :attr:`~telegram.ChatMember.LEFT`, + :attr:`~telegram.ChatMember.MEMBER` or :attr:`~telegram.ChatMember.RESTRICTED`. + + """ + + __slots__ = ("status", "user") + + ADMINISTRATOR: Final[str] = constants.ChatMemberStatus.ADMINISTRATOR + """:const:`telegram.constants.ChatMemberStatus.ADMINISTRATOR`""" + OWNER: Final[str] = constants.ChatMemberStatus.OWNER + """:const:`telegram.constants.ChatMemberStatus.OWNER`""" + BANNED: Final[str] = constants.ChatMemberStatus.BANNED + """:const:`telegram.constants.ChatMemberStatus.BANNED`""" + LEFT: Final[str] = constants.ChatMemberStatus.LEFT + """:const:`telegram.constants.ChatMemberStatus.LEFT`""" + MEMBER: Final[str] = constants.ChatMemberStatus.MEMBER + """:const:`telegram.constants.ChatMemberStatus.MEMBER`""" + RESTRICTED: Final[str] = constants.ChatMemberStatus.RESTRICTED + """:const:`telegram.constants.ChatMemberStatus.RESTRICTED`""" + + def __init__( + self, + user: User, + status: str, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required by all subclasses + self.user: User = user + self.status: str = status + + self._id_attrs = (self.user, self.status) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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, + cls.MEMBER: ChatMemberMember, + cls.RESTRICTED: ChatMemberRestricted, + cls.LEFT: ChatMemberLeft, + cls.BANNED: ChatMemberBanned, + } + + 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) + 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) + + # This is a deprecated field that TG still returns for backwards compatibility + # Let's filter it out to speed up the de-json process + if cls is ChatMemberRestricted and data.get("can_send_media_messages") is not None: + api_kwargs = {"can_send_media_messages": data.pop("can_send_media_messages")} + return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs) + + return super().de_json(data=data, bot=bot) + + +class ChatMemberOwner(ChatMember): + """ + Represents a chat member that owns the chat + and has all administrator privileges. + + .. versionadded:: 13.7 + + Args: + user (:class:`telegram.User`): Information about the user. + is_anonymous (:obj:`bool`): :obj:`True`, if the + user's presence in the chat is hidden. + custom_title (:obj:`str`, optional): Custom title for this user. + + Attributes: + status (:obj:`str`): The member's status in the chat, + always :tg-const:`telegram.ChatMember.OWNER`. + user (:class:`telegram.User`): Information about the user. + is_anonymous (:obj:`bool`): :obj:`True`, if the user's + presence in the chat is hidden. + custom_title (:obj:`str`): Optional. Custom title for + this user. + """ + + __slots__ = ("custom_title", "is_anonymous") + + def __init__( + self, + user: User, + is_anonymous: bool, + custom_title: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(status=ChatMember.OWNER, user=user, api_kwargs=api_kwargs) + with self._unfrozen(): + self.is_anonymous: bool = is_anonymous + self.custom_title: Optional[str] = custom_title + + +class ChatMemberAdministrator(ChatMember): + """ + Represents a chat member that has some additional privileges. + + .. versionadded:: 13.7 + .. versionchanged:: 20.0 + + * Argument and attribute ``can_manage_voice_chats`` were renamed to + :paramref:`can_manage_video_chats` and :attr:`can_manage_video_chats` in accordance to + Bot API 6.0. + * The argument :paramref:`can_manage_topics` was added, which changes the position of the + optional argument :paramref:`custom_title`. + + .. versionchanged:: 21.1 + As of this version, :attr:`can_post_stories`, :attr:`can_edit_stories`, + and :attr:`can_delete_stories` is now required. Thus, the order of arguments had to be + changed. + + Args: + user (:class:`telegram.User`): Information about the user. + can_be_edited (:obj:`bool`): :obj:`True`, if the bot + is allowed to edit administrator privileges of that user. + is_anonymous (:obj:`bool`): :obj:`True`, if the user's + presence in the chat is hidden. + can_manage_chat (:obj:`bool`): :obj:`True`, if the administrator can access the chat event + log, get boost list, see hidden supergroup and channel members, report spam messages + and ignore slow mode. Implied by any other administrator privilege. + can_delete_messages (:obj:`bool`): :obj:`True`, if the + administrator can delete messages of other users. + can_manage_video_chats (:obj:`bool`): :obj:`True`, if the + administrator can manage video chats. + + .. versionadded:: 20.0 + can_restrict_members (:obj:`bool`): :obj:`True`, if the + administrator can restrict, ban or unban chat members. + can_promote_members (:obj:`bool`): :obj:`True`, if the administrator + can add new administrators with a subset of his own privileges or demote + administrators that he has promoted, directly or indirectly (promoted by + administrators that were appointed by the user). + can_change_info (:obj:`bool`): :obj:`True`, if the user can change + the chat title, photo and other settings. + can_invite_users (:obj:`bool`): :obj:`True`, if the user can invite + new users to the chat. + can_post_messages (:obj:`bool`, optional): :obj:`True`, if the + administrator can post messages in the channel, or access channel statistics; + for channels only. + can_edit_messages (:obj:`bool`, optional): :obj:`True`, if the + administrator can edit messages of other users and can pin + messages; for channels only. + can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed + to pin messages; for groups and supergroups only. + can_post_stories (:obj:`bool`): :obj:`True`, if the administrator can post + stories to the chat. + + .. versionadded:: 20.6 + .. versionchanged:: 21.0 + |non_optional_story_argument| + can_edit_stories (:obj:`bool`): :obj:`True`, if the administrator can edit stories posted + by other users, post stories to the chat page, pin chat stories, and access the chat's + story archive + + .. versionadded:: 20.6 + .. versionchanged:: 21.0 + |non_optional_story_argument| + can_delete_stories (:obj:`bool`): :obj:`True`, if the administrator can delete + stories posted by other users. + + .. versionadded:: 20.6 + .. versionchanged:: 21.0 + |non_optional_story_argument| + can_manage_topics (:obj:`bool`, optional): :obj:`True`, if the user is allowed + to create, rename, close, and reopen forum topics; for supergroups only. + + .. versionadded:: 20.0 + custom_title (:obj:`str`, optional): Custom title for this user. + + Attributes: + status (:obj:`str`): The member's status in the chat, + always :tg-const:`telegram.ChatMember.ADMINISTRATOR`. + user (:class:`telegram.User`): Information about the user. + can_be_edited (:obj:`bool`): :obj:`True`, if the bot + is allowed to edit administrator privileges of that user. + is_anonymous (:obj:`bool`): :obj:`True`, if the user's + presence in the chat is hidden. + can_manage_chat (:obj:`bool`): :obj:`True`, if the administrator can access the chat event + log, get boost list, see hidden supergroup and channel members, report spam messages + and ignore slow mode. Implied by any other administrator privilege. + can_delete_messages (:obj:`bool`): :obj:`True`, if the + administrator can delete messages of other users. + can_manage_video_chats (:obj:`bool`): :obj:`True`, if the + administrator can manage video chats. + + .. versionadded:: 20.0 + can_restrict_members (:obj:`bool`): :obj:`True`, if the + administrator can restrict, ban or unban chat members, or access supergroup statistics. + can_promote_members (:obj:`bool`): :obj:`True`, if the administrator can add new + administrators with a subset of their own privileges or demote administrators + that they have promoted, directly or indirectly (promoted by administrators that + were appointed by the user). + can_change_info (:obj:`bool`): :obj:`True`, if the user can change + the chat title, photo and other settings. + can_invite_users (:obj:`bool`): :obj:`True`, if the user can invite + new users to the chat. + can_post_messages (:obj:`bool`): Optional. :obj:`True`, if the + administrator can post messages in the channel or access channel statistics; + for channels only. + can_edit_messages (:obj:`bool`): Optional. :obj:`True`, if the + administrator can edit messages of other users and can pin + messages; for channels only. + can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed + to pin messages; for groups and supergroups only. + can_post_stories (:obj:`bool`): :obj:`True`, if the administrator can post + stories to the chat. + + .. versionadded:: 20.6 + .. versionchanged:: 21.0 + |non_optional_story_argument| + can_edit_stories (:obj:`bool`): :obj:`True`, if the administrator can edit stories posted + by other users, post stories to the chat page, pin chat stories, and access the chat's + story archive + + .. versionadded:: 20.6 + .. versionchanged:: 21.0 + |non_optional_story_argument| + can_delete_stories (:obj:`bool`): :obj:`True`, if the administrator can delete + stories posted by other users. + + .. versionadded:: 20.6 + .. versionchanged:: 21.0 + |non_optional_story_argument| + can_manage_topics (:obj:`bool`): Optional. :obj:`True`, if the user is allowed + to create, rename, close, and reopen forum topics; for supergroups only + + .. versionadded:: 20.0 + custom_title (:obj:`str`): Optional. Custom title for this user. + """ + + __slots__ = ( + "can_be_edited", + "can_change_info", + "can_delete_messages", + "can_delete_stories", + "can_edit_messages", + "can_edit_stories", + "can_invite_users", + "can_manage_chat", + "can_manage_topics", + "can_manage_video_chats", + "can_pin_messages", + "can_post_messages", + "can_post_stories", + "can_promote_members", + "can_restrict_members", + "custom_title", + "is_anonymous", + ) + + def __init__( + self, + user: User, + can_be_edited: bool, + is_anonymous: bool, + can_manage_chat: bool, + can_delete_messages: bool, + can_manage_video_chats: bool, + can_restrict_members: bool, + can_promote_members: bool, + can_change_info: bool, + can_invite_users: bool, + can_post_stories: bool, + can_edit_stories: bool, + can_delete_stories: bool, + can_post_messages: Optional[bool] = None, + can_edit_messages: Optional[bool] = None, + can_pin_messages: Optional[bool] = None, + can_manage_topics: Optional[bool] = None, + custom_title: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(status=ChatMember.ADMINISTRATOR, user=user, api_kwargs=api_kwargs) + with self._unfrozen(): + self.can_be_edited: bool = can_be_edited + self.is_anonymous: bool = is_anonymous + self.can_manage_chat: bool = can_manage_chat + self.can_delete_messages: bool = can_delete_messages + self.can_manage_video_chats: bool = can_manage_video_chats + self.can_restrict_members: bool = can_restrict_members + self.can_promote_members: bool = can_promote_members + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_post_stories: bool = can_post_stories + self.can_edit_stories: bool = can_edit_stories + self.can_delete_stories: bool = can_delete_stories + # Optionals + self.can_post_messages: Optional[bool] = can_post_messages + self.can_edit_messages: Optional[bool] = can_edit_messages + self.can_pin_messages: Optional[bool] = can_pin_messages + self.can_manage_topics: Optional[bool] = can_manage_topics + self.custom_title: Optional[str] = custom_title + + +class ChatMemberMember(ChatMember): + """ + Represents a chat member that has no additional + privileges or restrictions. + + .. versionadded:: 13.7 + + Args: + user (:class:`telegram.User`): Information about the user. + until_date (:class:`datetime.datetime`, optional): Date when the user's subscription will + expire. + + .. versionadded:: 21.5 + + Attributes: + status (:obj:`str`): The member's status in the chat, + always :tg-const:`telegram.ChatMember.MEMBER`. + user (:class:`telegram.User`): Information about the user. + until_date (:class:`datetime.datetime`): Optional. Date when the user's subscription will + expire. + + .. versionadded:: 21.5 + + """ + + __slots__ = ("until_date",) + + def __init__( + self, + user: User, + until_date: Optional[datetime.datetime] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(status=ChatMember.MEMBER, user=user, api_kwargs=api_kwargs) + with self._unfrozen(): + self.until_date: Optional[datetime.datetime] = until_date + + +class ChatMemberRestricted(ChatMember): + """ + Represents a chat member that is under certain restrictions + in the chat. Supergroups only. + + .. versionadded:: 13.7 + .. versionchanged:: 20.0 + All arguments were made positional and their order was changed. + The argument can_manage_topics was added. + + .. versionchanged:: 20.5 + Removed deprecated argument and attribute ``can_send_media_messages``. + + Args: + user (:class:`telegram.User`): Information about the user. + is_member (:obj:`bool`): :obj:`True`, if the user is a + member of the chat at the moment of the request. + can_change_info (:obj:`bool`): :obj:`True`, if the user can change + the chat title, photo and other settings. + can_invite_users (:obj:`bool`): :obj:`True`, if the user can invite + new users to the chat. + can_pin_messages (:obj:`bool`): :obj:`True`, if the user is allowed + to pin messages; groups and supergroups only. + can_send_messages (:obj:`bool`): :obj:`True`, if the user is allowed + to send text messages, contacts, invoices, locations and venues. + can_send_polls (:obj:`bool`): :obj:`True`, if the user is allowed + to send polls. + can_send_other_messages (:obj:`bool`): :obj:`True`, if the user is allowed + to send animations, games, stickers and use inline bots. + can_add_web_page_previews (:obj:`bool`): :obj:`True`, if the user is + allowed to add web page previews to their messages. + can_manage_topics (:obj:`bool`): :obj:`True`, if the user is allowed to create + forum topics. + + .. versionadded:: 20.0 + until_date (:class:`datetime.datetime`): Date when restrictions + will be lifted for this user. + + .. versionchanged:: 20.3 + |datetime_localization| + can_send_audios (:obj:`bool`): :obj:`True`, if the user is allowed to send audios. + + .. versionadded:: 20.1 + can_send_documents (:obj:`bool`): :obj:`True`, if the user is allowed to send documents. + + .. versionadded:: 20.1 + can_send_photos (:obj:`bool`): :obj:`True`, if the user is allowed to send photos. + + .. versionadded:: 20.1 + can_send_videos (:obj:`bool`): :obj:`True`, if the user is allowed to send videos. + + .. versionadded:: 20.1 + can_send_video_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send video + notes. + + .. versionadded:: 20.1 + can_send_voice_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send voice + notes. + + .. versionadded:: 20.1 + + Attributes: + status (:obj:`str`): The member's status in the chat, + always :tg-const:`telegram.ChatMember.RESTRICTED`. + user (:class:`telegram.User`): Information about the user. + is_member (:obj:`bool`): :obj:`True`, if the user is a + member of the chat at the moment of the request. + can_change_info (:obj:`bool`): :obj:`True`, if the user can change + the chat title, photo and other settings. + can_invite_users (:obj:`bool`): :obj:`True`, if the user can invite + new users to the chat. + can_pin_messages (:obj:`bool`): :obj:`True`, if the user is allowed + to pin messages; groups and supergroups only. + can_send_messages (:obj:`bool`): :obj:`True`, if the user is allowed + to send text messages, contacts, locations and venues. + can_send_polls (:obj:`bool`): :obj:`True`, if the user is allowed + to send polls. + can_send_other_messages (:obj:`bool`): :obj:`True`, if the user is allowed + to send animations, games, stickers and use inline bots. + can_add_web_page_previews (:obj:`bool`): :obj:`True`, if the user is + allowed to add web page previews to their messages. + can_manage_topics (:obj:`bool`): :obj:`True`, if the user is allowed to create + forum topics. + + .. versionadded:: 20.0 + until_date (:class:`datetime.datetime`): Date when restrictions + will be lifted for this user. + + .. versionchanged:: 20.3 + |datetime_localization| + can_send_audios (:obj:`bool`): :obj:`True`, if the user is allowed to send audios. + + .. versionadded:: 20.1 + can_send_documents (:obj:`bool`): :obj:`True`, if the user is allowed to send documents. + + .. versionadded:: 20.1 + can_send_photos (:obj:`bool`): :obj:`True`, if the user is allowed to send photos. + + .. versionadded:: 20.1 + can_send_videos (:obj:`bool`): :obj:`True`, if the user is allowed to send videos. + + .. versionadded:: 20.1 + can_send_video_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send video + notes. + + .. versionadded:: 20.1 + can_send_voice_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send voice + notes. + + .. versionadded:: 20.1 + + """ + + __slots__ = ( + "can_add_web_page_previews", + "can_change_info", + "can_invite_users", + "can_manage_topics", + "can_pin_messages", + "can_send_audios", + "can_send_documents", + "can_send_messages", + "can_send_other_messages", + "can_send_photos", + "can_send_polls", + "can_send_video_notes", + "can_send_videos", + "can_send_voice_notes", + "is_member", + "until_date", + ) + + def __init__( + self, + user: User, + is_member: bool, + can_change_info: bool, + can_invite_users: bool, + can_pin_messages: bool, + can_send_messages: bool, + can_send_polls: bool, + can_send_other_messages: bool, + can_add_web_page_previews: bool, + can_manage_topics: bool, + until_date: datetime.datetime, + can_send_audios: bool, + can_send_documents: bool, + can_send_photos: bool, + can_send_videos: bool, + can_send_video_notes: bool, + can_send_voice_notes: bool, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(status=ChatMember.RESTRICTED, user=user, api_kwargs=api_kwargs) + with self._unfrozen(): + self.is_member: bool = is_member + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_pin_messages: bool = can_pin_messages + self.can_send_messages: bool = can_send_messages + self.can_send_polls: bool = can_send_polls + self.can_send_other_messages: bool = can_send_other_messages + self.can_add_web_page_previews: bool = can_add_web_page_previews + self.can_manage_topics: bool = can_manage_topics + self.until_date: datetime.datetime = until_date + self.can_send_audios: bool = can_send_audios + self.can_send_documents: bool = can_send_documents + self.can_send_photos: bool = can_send_photos + self.can_send_videos: bool = can_send_videos + self.can_send_video_notes: bool = can_send_video_notes + self.can_send_voice_notes: bool = can_send_voice_notes + + +class ChatMemberLeft(ChatMember): + """ + Represents a chat member that isn't currently a member of the chat, + but may join it themselves. + + .. versionadded:: 13.7 + + Args: + user (:class:`telegram.User`): Information about the user. + + Attributes: + status (:obj:`str`): The member's status in the chat, + always :tg-const:`telegram.ChatMember.LEFT`. + user (:class:`telegram.User`): Information about the user. + """ + + __slots__ = () + + def __init__( + self, + user: User, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(status=ChatMember.LEFT, user=user, api_kwargs=api_kwargs) + self._freeze() + + +class ChatMemberBanned(ChatMember): + """ + Represents a chat member that was banned in the chat and + can't return to the chat or view chat messages. + + .. versionadded:: 13.7 + + Args: + user (:class:`telegram.User`): Information about the user. + until_date (:class:`datetime.datetime`): Date when restrictions + will be lifted for this user. + + .. versionchanged:: 20.3 + |datetime_localization| + + Attributes: + status (:obj:`str`): The member's status in the chat, + always :tg-const:`telegram.ChatMember.BANNED`. + user (:class:`telegram.User`): Information about the user. + until_date (:class:`datetime.datetime`): Date when restrictions + will be lifted for this user. + + .. versionchanged:: 20.3 + |datetime_localization| + + """ + + __slots__ = ("until_date",) + + def __init__( + self, + user: User, + until_date: datetime.datetime, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(status=ChatMember.BANNED, user=user, api_kwargs=api_kwargs) + with self._unfrozen(): + self.until_date: datetime.datetime = until_date diff --git a/_chatmemberupdated.py b/_chatmemberupdated.py new file mode 100644 index 0000000000000000000000000000000000000000..1aacb2185338abba37c497c7de4ecd3d350fdf3b --- /dev/null +++ b/_chatmemberupdated.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram ChatMemberUpdated.""" +import datetime +from typing import TYPE_CHECKING, Dict, Optional, Tuple, Union + +from telegram._chat import Chat +from telegram._chatinvitelink import ChatInviteLink +from telegram._chatmember import ChatMember +from telegram._telegramobject import TelegramObject +from telegram._user import User +from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class ChatMemberUpdated(TelegramObject): + """This object represents changes in the status of a chat member. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`chat`, :attr:`from_user`, :attr:`date`, + :attr:`old_chat_member` and :attr:`new_chat_member` are equal. + + .. versionadded:: 13.4 + + Note: + In Python :keyword:`from` is a reserved word. Use :paramref:`from_user` instead. + + Examples: + :any:`Chat Member Bot ` + + Args: + chat (:class:`telegram.Chat`): Chat the user belongs to. + from_user (:class:`telegram.User`): Performer of the action, which resulted in the change. + date (:class:`datetime.datetime`): Date the change was done in Unix time. Converted to + :class:`datetime.datetime`. + + .. versionchanged:: 20.3 + |datetime_localization| + old_chat_member (:class:`telegram.ChatMember`): Previous information about the chat member. + new_chat_member (:class:`telegram.ChatMember`): New information about the chat member. + invite_link (:class:`telegram.ChatInviteLink`, optional): Chat invite link, which was used + by the user to join the chat. For joining by invite link events only. + via_chat_folder_invite_link (:obj:`bool`, optional): :obj:`True`, if the user joined the + chat via a chat folder invite link + + .. versionadded:: 20.3 + via_join_request (:obj:`bool`, optional): :obj:`True`, if the user joined the chat after + sending a direct join request without using an invite link and being approved by + an administrator + + .. versionadded:: 21.2 + + Attributes: + chat (:class:`telegram.Chat`): Chat the user belongs to. + from_user (:class:`telegram.User`): Performer of the action, which resulted in the change. + date (:class:`datetime.datetime`): Date the change was done in Unix time. Converted to + :class:`datetime.datetime`. + + .. versionchanged:: 20.3 + |datetime_localization| + old_chat_member (:class:`telegram.ChatMember`): Previous information about the chat member. + new_chat_member (:class:`telegram.ChatMember`): New information about the chat member. + invite_link (:class:`telegram.ChatInviteLink`): Optional. Chat invite link, which was used + by the user to join the chat. For joining by invite link events only. + via_chat_folder_invite_link (:obj:`bool`): Optional. :obj:`True`, if the user joined the + chat via a chat folder invite link + + .. versionadded:: 20.3 + via_join_request (:obj:`bool`): Optional. :obj:`True`, if the user joined the chat after + sending a direct join request without using an invite link and being approved + by an administrator + + .. versionadded:: 21.2 + + """ + + __slots__ = ( + "chat", + "date", + "from_user", + "invite_link", + "new_chat_member", + "old_chat_member", + "via_chat_folder_invite_link", + "via_join_request", + ) + + def __init__( + self, + chat: Chat, + from_user: User, + date: datetime.datetime, + old_chat_member: ChatMember, + new_chat_member: ChatMember, + invite_link: Optional[ChatInviteLink] = None, + via_chat_folder_invite_link: Optional[bool] = None, + via_join_request: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.chat: Chat = chat + self.from_user: User = from_user + self.date: datetime.datetime = date + self.old_chat_member: ChatMember = old_chat_member + self.new_chat_member: ChatMember = new_chat_member + self.via_chat_folder_invite_link: Optional[bool] = via_chat_folder_invite_link + + # Optionals + self.invite_link: Optional[ChatInviteLink] = invite_link + self.via_join_request: Optional[bool] = via_join_request + + self._id_attrs = ( + self.chat, + self.from_user, + self.date, + self.old_chat_member, + self.new_chat_member, + ) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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["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) + + return super().de_json(data=data, bot=bot) + + def _get_attribute_difference(self, attribute: str) -> Tuple[object, object]: + try: + old = self.old_chat_member[attribute] + except KeyError: + old = None + + try: + new = self.new_chat_member[attribute] + except KeyError: + new = None + + return old, new + + def difference( + self, + ) -> Dict[ + str, + Tuple[ + Union[str, bool, datetime.datetime, User], Union[str, bool, datetime.datetime, User] + ], + ]: + """Computes the difference between :attr:`old_chat_member` and :attr:`new_chat_member`. + + Example: + .. code:: pycon + + >>> chat_member_updated.difference() + {'custom_title': ('old title', 'new title')} + + Note: + To determine, if the :attr:`telegram.ChatMember.user` attribute has changed, *every* + attribute of the user will be checked. + + .. versionadded:: 13.5 + + Returns: + Dict[:obj:`str`, Tuple[:class:`object`, :class:`object`]]: A dictionary mapping + attribute names to tuples of the form ``(old_value, new_value)`` + """ + # we first get the names of the attributes that have changed + # user.to_dict() is unhashable, so that needs some special casing further down + old_dict = self.old_chat_member.to_dict() + old_user_dict = old_dict.pop("user") + new_dict = self.new_chat_member.to_dict() + new_user_dict = new_dict.pop("user") + + # Generator for speed: we only need to iterate over it once + # we can't directly use the values from old_dict ^ new_dict b/c that set is unordered + attributes = (entry[0] for entry in set(old_dict.items()) ^ set(new_dict.items())) + + result = {attribute: self._get_attribute_difference(attribute) for attribute in attributes} + if old_user_dict != new_user_dict: + result["user"] = (self.old_chat_member.user, self.new_chat_member.user) + + return result # type: ignore[return-value] diff --git a/_chatpermissions.py b/_chatpermissions.py new file mode 100644 index 0000000000000000000000000000000000000000..c4e9e94b7a937d7468ca059dc3ef42f633aee8b0 --- /dev/null +++ b/_chatpermissions.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram ChatPermission.""" +from typing import TYPE_CHECKING, Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class ChatPermissions(TelegramObject): + """Describes actions that a non-administrator user is allowed to take in a chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`can_send_messages`, + :attr:`can_send_polls`, :attr:`can_send_other_messages`, :attr:`can_add_web_page_previews`, + :attr:`can_change_info`, :attr:`can_invite_users`, :attr:`can_pin_messages`, + :attr:`can_send_audios`, :attr:`can_send_documents`, :attr:`can_send_photos`, + :attr:`can_send_videos`, :attr:`can_send_video_notes`, :attr:`can_send_voice_notes`, and + :attr:`can_manage_topics` are equal. + + .. versionchanged:: 20.0 + :attr:`can_manage_topics` is considered as well when comparing objects of + this type in terms of equality. + .. versionchanged:: 20.5 + + * :attr:`can_send_audios`, :attr:`can_send_documents`, :attr:`can_send_photos`, + :attr:`can_send_videos`, :attr:`can_send_video_notes` and :attr:`can_send_voice_notes` + are considered as well when comparing objects of this type in terms of equality. + * Removed deprecated argument and attribute ``can_send_media_messages``. + + + Note: + Though not stated explicitly in the official docs, Telegram changes not only the + permissions that are set, but also sets all the others to :obj:`False`. However, since not + documented, this behavior may change unbeknown to PTB. + + Args: + can_send_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to send text + messages, contacts, locations and venues. + can_send_polls (:obj:`bool`, optional): :obj:`True`, if the user is allowed to send polls. + can_send_other_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to + send animations, games, stickers and use inline bots. + can_add_web_page_previews (:obj:`bool`, optional): :obj:`True`, if the user is allowed to + add web page previews to their messages. + can_change_info (:obj:`bool`, optional): :obj:`True`, if the user is allowed to change the + chat title, photo and other settings. Ignored in public supergroups. + can_invite_users (:obj:`bool`, optional): :obj:`True`, if the user is allowed to invite new + users to the chat. + can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to pin + messages. Ignored in public supergroups. + can_manage_topics (:obj:`bool`, optional): :obj:`True`, if the user is allowed + to create forum topics. If omitted defaults to the value of + :attr:`can_pin_messages`. + + .. versionadded:: 20.0 + can_send_audios (:obj:`bool`): :obj:`True`, if the user is allowed to send audios. + + .. versionadded:: 20.1 + can_send_documents (:obj:`bool`): :obj:`True`, if the user is allowed to send documents. + + .. versionadded:: 20.1 + can_send_photos (:obj:`bool`): :obj:`True`, if the user is allowed to send photos. + + .. versionadded:: 20.1 + can_send_videos (:obj:`bool`): :obj:`True`, if the user is allowed to send videos. + + .. versionadded:: 20.1 + can_send_video_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send video + notes. + + .. versionadded:: 20.1 + can_send_voice_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send voice + notes. + + .. versionadded:: 20.1 + + Attributes: + can_send_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to send text + messages, contacts, locations and venues. + can_send_polls (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to send polls, + implies :attr:`can_send_messages`. + can_send_other_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to + send animations, games, stickers and use inline bots. + can_add_web_page_previews (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to + add web page previews to their messages. + can_change_info (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to change the + chat title, photo and other settings. Ignored in public supergroups. + can_invite_users (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to invite + new users to the chat. + can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to pin + messages. Ignored in public supergroups. + can_manage_topics (:obj:`bool`): Optional. :obj:`True`, if the user is allowed + to create forum topics. If omitted defaults to the value of + :attr:`can_pin_messages`. + + .. versionadded:: 20.0 + can_send_audios (:obj:`bool`): :obj:`True`, if the user is allowed to send audios. + + .. versionadded:: 20.1 + can_send_documents (:obj:`bool`): :obj:`True`, if the user is allowed to send documents. + + .. versionadded:: 20.1 + can_send_photos (:obj:`bool`): :obj:`True`, if the user is allowed to send photos. + + .. versionadded:: 20.1 + can_send_videos (:obj:`bool`): :obj:`True`, if the user is allowed to send videos. + + .. versionadded:: 20.1 + can_send_video_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send video + notes. + + .. versionadded:: 20.1 + can_send_voice_notes (:obj:`bool`): :obj:`True`, if the user is allowed to send voice + notes. + + .. versionadded:: 20.1 + + """ + + __slots__ = ( + "can_add_web_page_previews", + "can_change_info", + "can_invite_users", + "can_manage_topics", + "can_pin_messages", + "can_send_audios", + "can_send_documents", + "can_send_messages", + "can_send_other_messages", + "can_send_photos", + "can_send_polls", + "can_send_video_notes", + "can_send_videos", + "can_send_voice_notes", + ) + + def __init__( + self, + can_send_messages: Optional[bool] = None, + can_send_polls: Optional[bool] = None, + can_send_other_messages: Optional[bool] = None, + can_add_web_page_previews: Optional[bool] = None, + can_change_info: Optional[bool] = None, + can_invite_users: Optional[bool] = None, + can_pin_messages: Optional[bool] = None, + can_manage_topics: Optional[bool] = None, + can_send_audios: Optional[bool] = None, + can_send_documents: Optional[bool] = None, + can_send_photos: Optional[bool] = None, + can_send_videos: Optional[bool] = None, + can_send_video_notes: Optional[bool] = None, + can_send_voice_notes: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.can_send_messages: Optional[bool] = can_send_messages + self.can_send_polls: Optional[bool] = can_send_polls + self.can_send_other_messages: Optional[bool] = can_send_other_messages + self.can_add_web_page_previews: Optional[bool] = can_add_web_page_previews + self.can_change_info: Optional[bool] = can_change_info + self.can_invite_users: Optional[bool] = can_invite_users + self.can_pin_messages: Optional[bool] = can_pin_messages + self.can_manage_topics: Optional[bool] = can_manage_topics + self.can_send_audios: Optional[bool] = can_send_audios + self.can_send_documents: Optional[bool] = can_send_documents + self.can_send_photos: Optional[bool] = can_send_photos + self.can_send_videos: Optional[bool] = can_send_videos + self.can_send_video_notes: Optional[bool] = can_send_video_notes + self.can_send_voice_notes: Optional[bool] = can_send_voice_notes + + self._id_attrs = ( + self.can_send_messages, + self.can_send_polls, + self.can_send_other_messages, + self.can_add_web_page_previews, + self.can_change_info, + self.can_invite_users, + self.can_pin_messages, + self.can_manage_topics, + self.can_send_audios, + self.can_send_documents, + self.can_send_photos, + self.can_send_videos, + self.can_send_video_notes, + self.can_send_voice_notes, + ) + + self._freeze() + + @classmethod + def all_permissions(cls) -> "ChatPermissions": + """ + This method returns an :class:`ChatPermissions` instance with all attributes + set to :obj:`True`. This is e.g. useful when unrestricting a chat member with + :meth:`telegram.Bot.restrict_chat_member`. + + .. versionadded:: 20.0 + + """ + return cls(*(14 * (True,))) + + @classmethod + def no_permissions(cls) -> "ChatPermissions": + """ + This method returns an :class:`ChatPermissions` instance + with all attributes set to :obj:`False`. + + .. versionadded:: 20.0 + """ + return cls(*(14 * (False,))) + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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 + if data.get("can_send_media_messages") is not None: + api_kwargs["can_send_media_messages"] = data.pop("can_send_media_messages") + + return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs) diff --git a/_choseninlineresult.py b/_choseninlineresult.py new file mode 100644 index 0000000000000000000000000000000000000000..76380e958395a047fc61f2ba9bb5af987b392da3 --- /dev/null +++ b/_choseninlineresult.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python +# pylint: disable=too-many-arguments +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram ChosenInlineResult.""" + +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.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class ChosenInlineResult(TelegramObject): + """ + Represents a result of an inline query that was chosen by the user and sent to their chat + partner. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`result_id` is equal. + + Note: + * In Python :keyword:`from` is a reserved word. Use :paramref:`from_user` instead. + * It is necessary to enable inline feedback via `@Botfather `_ in + order to receive these objects in updates. + + Args: + result_id (:obj:`str`): The unique identifier for the result that was chosen. + from_user (:class:`telegram.User`): The user that chose the result. + location (:class:`telegram.Location`, optional): Sender location, only for bots that + require user location. + inline_message_id (:obj:`str`, optional): Identifier of the sent inline message. Available + only if there is an inline keyboard attached to the message. Will be also received in + callback queries and can be used to edit the message. + query (:obj:`str`): The query that was used to obtain the result. + + Attributes: + result_id (:obj:`str`): The unique identifier for the result that was chosen. + from_user (:class:`telegram.User`): The user that chose the result. + location (:class:`telegram.Location`): Optional. Sender location, only for bots that + require user location. + inline_message_id (:obj:`str`): Optional. Identifier of the sent inline message. Available + only if there is an inline keyboard attached to the message. Will be also received in + callback queries and can be used to edit the message. + query (:obj:`str`): The query that was used to obtain the result. + + """ + + __slots__ = ("from_user", "inline_message_id", "location", "query", "result_id") + + def __init__( + self, + result_id: str, + from_user: User, + query: str, + location: Optional[Location] = None, + inline_message_id: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + # Required + self.result_id: str = result_id + self.from_user: User = from_user + self.query: str = query + # Optionals + self.location: Optional[Location] = location + self.inline_message_id: Optional[str] = inline_message_id + + self._id_attrs = (self.result_id,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + # Optionals + data["location"] = Location.de_json(data.get("location"), bot) + + return super().de_json(data=data, bot=bot) diff --git a/_dice.py b/_dice.py new file mode 100644 index 0000000000000000000000000000000000000000..621e4b13f98b79b0a797bd7b7233c4a68e811773 --- /dev/null +++ b/_dice.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram Dice.""" +from typing import Final, List, Optional + +from telegram import constants +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class Dice(TelegramObject): + """ + This object represents an animated emoji with a random value for currently supported base + emoji. (The singular form of "dice" is "die". However, PTB mimics the Telegram API, which uses + the term "dice".) + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`value` and :attr:`emoji` are equal. + + Note: + If :attr:`emoji` is :tg-const:`telegram.Dice.DARTS`, a value of 6 currently + represents a bullseye, while a value of 1 indicates that the dartboard was missed. + However, this behaviour is undocumented and might be changed by Telegram. + + If :attr:`emoji` is :tg-const:`telegram.Dice.BASKETBALL`, a value of 4 or 5 + currently score a basket, while a value of 1 to 3 indicates that the basket was missed. + However, this behaviour is undocumented and might be changed by Telegram. + + If :attr:`emoji` is :tg-const:`telegram.Dice.FOOTBALL`, a value of 4 to 5 + currently scores a goal, while a value of 1 to 3 indicates that the goal was missed. + However, this behaviour is undocumented and might be changed by Telegram. + + If :attr:`emoji` is :tg-const:`telegram.Dice.BOWLING`, a value of 6 knocks + all the pins, while a value of 1 means all the pins were missed. + However, this behaviour is undocumented and might be changed by Telegram. + + If :attr:`emoji` is :tg-const:`telegram.Dice.SLOT_MACHINE`, each value + corresponds to a unique combination of symbols, which + can be found in our + :wiki:`wiki `. + However, this behaviour is undocumented and might be changed by Telegram. + + .. + In args, some links for limits of `value` intentionally point to constants for only + one emoji of a group to avoid duplication. For example, maximum value for Dice, Darts and + Bowling is linked to a constant for Bowling. + + Args: + value (:obj:`int`): Value of the dice. + :tg-const:`telegram.Dice.MIN_VALUE`-:tg-const:`telegram.Dice.MAX_VALUE_BOWLING` + for :tg-const:`telegram.Dice.DICE`, :tg-const:`telegram.Dice.DARTS` and + :tg-const:`telegram.Dice.BOWLING` base emoji, + :tg-const:`telegram.Dice.MIN_VALUE`-:tg-const:`telegram.Dice.MAX_VALUE_BASKETBALL` + for :tg-const:`telegram.Dice.BASKETBALL` and :tg-const:`telegram.Dice.FOOTBALL` + base emoji, + :tg-const:`telegram.Dice.MIN_VALUE`-:tg-const:`telegram.Dice.MAX_VALUE_SLOT_MACHINE` + for :tg-const:`telegram.Dice.SLOT_MACHINE` base emoji. + emoji (:obj:`str`): Emoji on which the dice throw animation is based. + + Attributes: + value (:obj:`int`): Value of the dice. + :tg-const:`telegram.Dice.MIN_VALUE`-:tg-const:`telegram.Dice.MAX_VALUE_BOWLING` + for :tg-const:`telegram.Dice.DICE`, :tg-const:`telegram.Dice.DARTS` and + :tg-const:`telegram.Dice.BOWLING` base emoji, + :tg-const:`telegram.Dice.MIN_VALUE`-:tg-const:`telegram.Dice.MAX_VALUE_BASKETBALL` + for :tg-const:`telegram.Dice.BASKETBALL` and :tg-const:`telegram.Dice.FOOTBALL` + base emoji, + :tg-const:`telegram.Dice.MIN_VALUE`-:tg-const:`telegram.Dice.MAX_VALUE_SLOT_MACHINE` + for :tg-const:`telegram.Dice.SLOT_MACHINE` base emoji. + emoji (:obj:`str`): Emoji on which the dice throw animation is based. + + """ + + __slots__ = ("emoji", "value") + + def __init__(self, value: int, emoji: str, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + self.value: int = value + self.emoji: str = emoji + + self._id_attrs = (self.value, self.emoji) + + self._freeze() + + DICE: Final[str] = constants.DiceEmoji.DICE + """:const:`telegram.constants.DiceEmoji.DICE`""" + DARTS: Final[str] = constants.DiceEmoji.DARTS + """:const:`telegram.constants.DiceEmoji.DARTS`""" + BASKETBALL: Final[str] = constants.DiceEmoji.BASKETBALL + """:const:`telegram.constants.DiceEmoji.BASKETBALL`""" + FOOTBALL: Final[str] = constants.DiceEmoji.FOOTBALL + """:const:`telegram.constants.DiceEmoji.FOOTBALL`""" + SLOT_MACHINE: Final[str] = constants.DiceEmoji.SLOT_MACHINE + """:const:`telegram.constants.DiceEmoji.SLOT_MACHINE`""" + BOWLING: Final[str] = constants.DiceEmoji.BOWLING + """ + :const:`telegram.constants.DiceEmoji.BOWLING` + + .. versionadded:: 13.4 + """ + ALL_EMOJI: Final[List[str]] = list(constants.DiceEmoji) + """List[:obj:`str`]: A list of all available dice emoji.""" + + MIN_VALUE: Final[int] = constants.DiceLimit.MIN_VALUE + """:const:`telegram.constants.DiceLimit.MIN_VALUE` + + .. versionadded:: 20.0 + """ + + MAX_VALUE_BOWLING: Final[int] = constants.DiceLimit.MAX_VALUE_BOWLING + """:const:`telegram.constants.DiceLimit.MAX_VALUE_BOWLING` + + .. versionadded:: 20.0 + """ + + MAX_VALUE_DARTS: Final[int] = constants.DiceLimit.MAX_VALUE_DARTS + """:const:`telegram.constants.DiceLimit.MAX_VALUE_DARTS` + + .. versionadded:: 20.0 + """ + + MAX_VALUE_DICE: Final[int] = constants.DiceLimit.MAX_VALUE_DICE + """:const:`telegram.constants.DiceLimit.MAX_VALUE_DICE` + + .. versionadded:: 20.0 + """ + + MAX_VALUE_BASKETBALL: Final[int] = constants.DiceLimit.MAX_VALUE_BASKETBALL + """:const:`telegram.constants.DiceLimit.MAX_VALUE_BASKETBALL` + + .. versionadded:: 20.0 + """ + + MAX_VALUE_FOOTBALL: Final[int] = constants.DiceLimit.MAX_VALUE_FOOTBALL + """:const:`telegram.constants.DiceLimit.MAX_VALUE_FOOTBALL` + + .. versionadded:: 20.0 + """ + + MAX_VALUE_SLOT_MACHINE: Final[int] = constants.DiceLimit.MAX_VALUE_SLOT_MACHINE + """:const:`telegram.constants.DiceLimit.MAX_VALUE_SLOT_MACHINE` + + .. versionadded:: 20.0 + """ diff --git a/_forcereply.py b/_forcereply.py new file mode 100644 index 0000000000000000000000000000000000000000..cce00996bbda415a5788f434a34af06f729b96be --- /dev/null +++ b/_forcereply.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram ForceReply.""" + +from typing import Final, Optional + +from telegram import constants +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class ForceReply(TelegramObject): + """ + Upon receiving a message with this object, Telegram clients will display a reply interface to + the user (act as if the user has selected the bot's message and tapped 'Reply'). This can be + extremely useful if you want to create user-friendly step-by-step interfaces without having + to sacrifice `privacy mode `_. Not + supported in channels and for messages sent on behalf of a Telegram Business account. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`selective` is equal. + + .. versionchanged:: 20.0 + The (undocumented) argument ``force_reply`` was removed and instead :attr:`force_reply` + is now always set to :obj:`True` as expected by the Bot API. + + Args: + selective (:obj:`bool`, optional): Use this parameter if you want to force reply from + specific users only. Targets: + + 1) Users that are @mentioned in the :attr:`~telegram.Message.text` of the + :class:`telegram.Message` object. + 2) If the bot's message is a reply to a message in the same chat and forum topic, + sender of the original message. + + input_field_placeholder (:obj:`str`, optional): The placeholder to be shown in the input + field when the reply is active; + :tg-const:`telegram.ForceReply.MIN_INPUT_FIELD_PLACEHOLDER`- + :tg-const:`telegram.ForceReply.MAX_INPUT_FIELD_PLACEHOLDER` + characters. + + .. versionadded:: 13.7 + + Attributes: + force_reply (:obj:`True`): Shows reply interface to the user, as if they manually selected + the bots message and tapped 'Reply'. + selective (:obj:`bool`): Optional. Force reply from specific users only. Targets: + + 1) Users that are @mentioned in the :attr:`~telegram.Message.text` of the + :class:`telegram.Message` object. + 2) If the bot's message is a reply to a message in the same chat and forum topic, + sender of the original message. + input_field_placeholder (:obj:`str`): Optional. The placeholder to be shown in the input + field when the reply is active; + :tg-const:`telegram.ForceReply.MIN_INPUT_FIELD_PLACEHOLDER`- + :tg-const:`telegram.ForceReply.MAX_INPUT_FIELD_PLACEHOLDER` + characters. + + .. versionadded:: 13.7 + + """ + + __slots__ = ("force_reply", "input_field_placeholder", "selective") + + def __init__( + self, + selective: Optional[bool] = None, + input_field_placeholder: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.force_reply: bool = True + self.selective: Optional[bool] = selective + self.input_field_placeholder: Optional[str] = input_field_placeholder + + self._id_attrs = (self.selective,) + + self._freeze() + + MIN_INPUT_FIELD_PLACEHOLDER: Final[int] = constants.ReplyLimit.MIN_INPUT_FIELD_PLACEHOLDER + """:const:`telegram.constants.ReplyLimit.MIN_INPUT_FIELD_PLACEHOLDER` + + .. versionadded:: 20.0 + """ + MAX_INPUT_FIELD_PLACEHOLDER: Final[int] = constants.ReplyLimit.MAX_INPUT_FIELD_PLACEHOLDER + """:const:`telegram.constants.ReplyLimit.MAX_INPUT_FIELD_PLACEHOLDER` + + .. versionadded:: 20.0 + """ diff --git a/_forumtopic.py b/_forumtopic.py new file mode 100644 index 0000000000000000000000000000000000000000..bd66e40d053554a963d7b2c17db8d51c5ab0b07b --- /dev/null +++ b/_forumtopic.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains objects related to Telegram forum topics.""" +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class ForumTopic(TelegramObject): + """ + This object represents a forum topic. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`message_thread_id`, :attr:`name` and :attr:`icon_color` + are equal. + + .. versionadded:: 20.0 + + Args: + message_thread_id (:obj:`int`): Unique identifier of the forum topic + name (:obj:`str`): Name of the topic + icon_color (:obj:`int`): Color of the topic icon in RGB format + icon_custom_emoji_id (:obj:`str`, optional): Unique identifier of the custom emoji shown + as the topic icon. + + Attributes: + message_thread_id (:obj:`int`): Unique identifier of the forum topic + name (:obj:`str`): Name of the topic + icon_color (:obj:`int`): Color of the topic icon in RGB format + icon_custom_emoji_id (:obj:`str`): Optional. Unique identifier of the custom emoji shown + as the topic icon. + """ + + __slots__ = ("icon_color", "icon_custom_emoji_id", "message_thread_id", "name") + + def __init__( + self, + message_thread_id: int, + name: str, + icon_color: int, + icon_custom_emoji_id: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.message_thread_id: int = message_thread_id + self.name: str = name + self.icon_color: int = icon_color + self.icon_custom_emoji_id: Optional[str] = icon_custom_emoji_id + + self._id_attrs = (self.message_thread_id, self.name, self.icon_color) + + self._freeze() + + +class ForumTopicCreated(TelegramObject): + """ + This object represents the content of a service message about a new forum topic created in + the chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`name` and :attr:`icon_color` are equal. + + .. versionadded:: 20.0 + + Args: + name (:obj:`str`): Name of the topic + icon_color (:obj:`int`): Color of the topic icon in RGB format + icon_custom_emoji_id (:obj:`str`, optional): Unique identifier of the custom emoji shown + as the topic icon. + + Attributes: + name (:obj:`str`): Name of the topic + icon_color (:obj:`int`): Color of the topic icon in RGB format + icon_custom_emoji_id (:obj:`str`): Optional. Unique identifier of the custom emoji shown + as the topic icon. + """ + + __slots__ = ("icon_color", "icon_custom_emoji_id", "name") + + def __init__( + self, + name: str, + icon_color: int, + icon_custom_emoji_id: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.name: str = name + self.icon_color: int = icon_color + self.icon_custom_emoji_id: Optional[str] = icon_custom_emoji_id + + self._id_attrs = (self.name, self.icon_color) + + self._freeze() + + +class ForumTopicClosed(TelegramObject): + """ + This object represents a service message about a forum topic closed in the chat. + Currently holds no information. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None: + super().__init__(api_kwargs=api_kwargs) + + self._freeze() + + +class ForumTopicReopened(TelegramObject): + """ + This object represents a service message about a forum topic reopened in the chat. + Currently holds no information. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None: + super().__init__(api_kwargs=api_kwargs) + + self._freeze() + + +class ForumTopicEdited(TelegramObject): + """ + This object represents a service message about an edited forum topic. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`name` and :attr:`icon_custom_emoji_id` are equal. + + .. versionadded:: 20.0 + + Args: + name (:obj:`str`, optional): New name of the topic, if it was edited. + icon_custom_emoji_id (:obj:`str`, optional): New identifier of the custom emoji shown as + the topic icon, if it was edited; an empty string if the icon was removed. + + Attributes: + name (:obj:`str`): Optional. New name of the topic, if it was edited. + icon_custom_emoji_id (:obj:`str`): Optional. New identifier of the custom emoji shown as + the topic icon, if it was edited; an empty string if the icon was removed. + """ + + __slots__ = ("icon_custom_emoji_id", "name") + + def __init__( + self, + name: Optional[str] = None, + icon_custom_emoji_id: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.name: Optional[str] = name + self.icon_custom_emoji_id: Optional[str] = icon_custom_emoji_id + + self._id_attrs = (self.name, self.icon_custom_emoji_id) + + self._freeze() + + +class GeneralForumTopicHidden(TelegramObject): + """ + This object represents a service message about General forum topic hidden in the chat. + Currently holds no information. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + + self._freeze() + + +class GeneralForumTopicUnhidden(TelegramObject): + """ + This object represents a service message about General forum topic unhidden in the chat. + Currently holds no information. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + + self._freeze() diff --git a/_giveaway.py b/_giveaway.py new file mode 100644 index 0000000000000000000000000000000000000000..b287433fe0b830d584ad2501fd234fa8a61d337b --- /dev/null +++ b/_giveaway.py @@ -0,0 +1,344 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an objects that are related to Telegram giveaways.""" +import datetime +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +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.datetime import extract_tzinfo_from_defaults, from_timestamp +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot, Message + + +class Giveaway(TelegramObject): + """This object represents a message about a scheduled giveaway. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`chats`, :attr:`winners_selection_date` and + :attr:`winner_count` are equal. + + .. versionadded:: 20.8 + + Args: + chats (Tuple[:class:`telegram.Chat`]): The list of chats which the user must join to + participate in the giveaway. + winners_selection_date (:class:`datetime.datetime`): The date when the giveaway winner will + be selected. |datetime_localization| + winner_count (:obj:`int`): The number of users which are supposed to be selected as winners + of the giveaway. + only_new_members (:obj:`True`, optional): If :obj:`True`, only users who join the chats + after the giveaway started should be eligible to win. + has_public_winners (:obj:`True`, optional): :obj:`True`, if the list of giveaway winners + will be visible to everyone + prize_description (:obj:`str`, optional): Description of additional giveaway prize + country_codes (Sequence[:obj:`str`]): A list of two-letter ISO 3166-1 alpha-2 + country codes indicating the countries from which eligible users for the giveaway must + come. If empty, then all users can participate in the giveaway. Users with a phone + number that was bought on Fragment can always participate in giveaways. + premium_subscription_month_count (:obj:`int`, optional): The number of months the Telegram + Premium subscription won from the giveaway will be active for. + + Attributes: + chats (Sequence[:class:`telegram.Chat`]): The list of chats which the user must join to + participate in the giveaway. + winners_selection_date (:class:`datetime.datetime`): The date when the giveaway winner will + be selected. |datetime_localization| + winner_count (:obj:`int`): The number of users which are supposed to be selected as winners + of the giveaway. + only_new_members (:obj:`True`): Optional. If :obj:`True`, only users who join the chats + after the giveaway started should be eligible to win. + has_public_winners (:obj:`True`): Optional. :obj:`True`, if the list of giveaway winners + will be visible to everyone + prize_description (:obj:`str`): Optional. Description of additional giveaway prize + country_codes (Tuple[:obj:`str`]): Optional. A tuple of two-letter ISO 3166-1 alpha-2 + country codes indicating the countries from which eligible users for the giveaway must + come. If empty, then all users can participate in the giveaway. Users with a phone + number that was bought on Fragment can always participate in giveaways. + premium_subscription_month_count (:obj:`int`): Optional. The number of months the Telegram + Premium subscription won from the giveaway will be active for. + """ + + __slots__ = ( + "chats", + "country_codes", + "has_public_winners", + "only_new_members", + "premium_subscription_month_count", + "prize_description", + "winner_count", + "winners_selection_date", + ) + + def __init__( + self, + chats: Sequence[Chat], + winners_selection_date: datetime.datetime, + winner_count: int, + only_new_members: Optional[bool] = None, + has_public_winners: Optional[bool] = None, + prize_description: Optional[str] = None, + country_codes: Optional[Sequence[str]] = None, + premium_subscription_month_count: Optional[int] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + self.chats: Tuple[Chat, ...] = tuple(chats) + self.winners_selection_date: datetime.datetime = winners_selection_date + self.winner_count: int = winner_count + self.only_new_members: Optional[bool] = only_new_members + self.has_public_winners: Optional[bool] = has_public_winners + self.prize_description: Optional[str] = prize_description + self.country_codes: Tuple[str, ...] = parse_sequence_arg(country_codes) + self.premium_subscription_month_count: Optional[int] = premium_subscription_month_count + + self._id_attrs = ( + self.chats, + self.winners_selection_date, + self.winner_count, + ) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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["winners_selection_date"] = from_timestamp( + data.get("winners_selection_date"), tzinfo=loc_tzinfo + ) + + return super().de_json(data=data, bot=bot) + + +class GiveawayCreated(TelegramObject): + """This object represents a service message about the creation of a scheduled giveaway. + Currently holds no information. + """ + + __slots__ = () + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + + self._freeze() + + +class GiveawayWinners(TelegramObject): + """This object represents a message about the completion of a giveaway with public winners. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`chat`, :attr:`giveaway_message_id`, + :attr:`winners_selection_date`, :attr:`winner_count` and :attr:`winners` are equal. + + .. versionadded:: 20.8 + + Args: + chat (:class:`telegram.Chat`): The chat that created the giveaway + giveaway_message_id (:obj:`int`): Identifier of the message with the giveaway in the chat + winners_selection_date (:class:`datetime.datetime`): Point in time when winners of the + giveaway were selected. |datetime_localization| + winner_count (:obj:`int`): Total number of winners in the giveaway + winners (Sequence[:class:`telegram.User`]): List of up to + :tg-const:`telegram.constants.GiveawayLimit.MAX_WINNERS` winners of the giveaway + additional_chat_count (:obj:`int`, optional): The number of other chats the user had to + join in order to be eligible for the giveaway + premium_subscription_month_count (:obj:`int`, optional): The number of months the Telegram + Premium subscription won from the giveaway will be active for + unclaimed_prize_count (:obj:`int`, optional): Number of undistributed prizes + only_new_members (:obj:`True`, optional): :obj:`True`, if only users who had joined the + chats after the giveaway started were eligible to win + was_refunded (:obj:`True`, optional): :obj:`True`, if the giveaway was canceled because the + payment for it was refunded + prize_description (:obj:`str`, optional): Description of additional giveaway prize + + Attributes: + chat (:class:`telegram.Chat`): The chat that created the giveaway + giveaway_message_id (:obj:`int`): Identifier of the message with the giveaway in the chat + winners_selection_date (:class:`datetime.datetime`): Point in time when winners of the + giveaway were selected. |datetime_localization| + winner_count (:obj:`int`): Total number of winners in the giveaway + winners (Tuple[:class:`telegram.User`]): tuple of up to + :tg-const:`telegram.constants.GiveawayLimit.MAX_WINNERS` winners of the giveaway + additional_chat_count (:obj:`int`): Optional. The number of other chats the user had to + join in order to be eligible for the giveaway + premium_subscription_month_count (:obj:`int`): Optional. The number of months the Telegram + Premium subscription won from the giveaway will be active for + unclaimed_prize_count (:obj:`int`): Optional. Number of undistributed prizes + only_new_members (:obj:`True`): Optional. :obj:`True`, if only users who had joined the + chats after the giveaway started were eligible to win + was_refunded (:obj:`True`): Optional. :obj:`True`, if the giveaway was canceled because the + payment for it was refunded + prize_description (:obj:`str`): Optional. Description of additional giveaway prize + """ + + __slots__ = ( + "additional_chat_count", + "chat", + "giveaway_message_id", + "only_new_members", + "premium_subscription_month_count", + "prize_description", + "unclaimed_prize_count", + "was_refunded", + "winner_count", + "winners", + "winners_selection_date", + ) + + def __init__( + self, + chat: Chat, + giveaway_message_id: int, + winners_selection_date: datetime.datetime, + winner_count: int, + winners: Sequence[User], + additional_chat_count: Optional[int] = None, + premium_subscription_month_count: Optional[int] = None, + unclaimed_prize_count: Optional[int] = None, + only_new_members: Optional[bool] = None, + was_refunded: Optional[bool] = None, + prize_description: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + self.chat: Chat = chat + self.giveaway_message_id: int = giveaway_message_id + self.winners_selection_date: datetime.datetime = winners_selection_date + self.winner_count: int = winner_count + self.winners: Tuple[User, ...] = tuple(winners) + self.additional_chat_count: Optional[int] = additional_chat_count + self.premium_subscription_month_count: Optional[int] = premium_subscription_month_count + self.unclaimed_prize_count: Optional[int] = unclaimed_prize_count + self.only_new_members: Optional[bool] = only_new_members + self.was_refunded: Optional[bool] = was_refunded + self.prize_description: Optional[str] = prize_description + + self._id_attrs = ( + self.chat, + self.giveaway_message_id, + self.winners_selection_date, + self.winner_count, + self.winners, + ) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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["winners_selection_date"] = from_timestamp( + data.get("winners_selection_date"), tzinfo=loc_tzinfo + ) + + return super().de_json(data=data, bot=bot) + + +class GiveawayCompleted(TelegramObject): + """This object represents a service message about the completion of a giveaway without public + winners. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`winner_count` and :attr:`unclaimed_prize_count` are equal. + + .. versionadded:: 20.8 + + + Args: + winner_count (:obj:`int`): Number of winners in the giveaway + unclaimed_prize_count (:obj:`int`, optional): Number of undistributed prizes + giveaway_message (:class:`telegram.Message`, optional): Message with the giveaway that was + completed, if it wasn't deleted + + Attributes: + winner_count (:obj:`int`): Number of winners in the giveaway + unclaimed_prize_count (:obj:`int`): Optional. Number of undistributed prizes + giveaway_message (:class:`telegram.Message`): Optional. Message with the giveaway that was + completed, if it wasn't deleted + """ + + __slots__ = ("giveaway_message", "unclaimed_prize_count", "winner_count") + + def __init__( + self, + winner_count: int, + unclaimed_prize_count: Optional[int] = None, + giveaway_message: Optional["Message"] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + self.winner_count: int = winner_count + self.unclaimed_prize_count: Optional[int] = unclaimed_prize_count + self.giveaway_message: Optional[Message] = giveaway_message + + self._id_attrs = ( + self.winner_count, + self.unclaimed_prize_count, + ) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) diff --git a/_keyboardbutton.py b/_keyboardbutton.py new file mode 100644 index 0000000000000000000000000000000000000000..ad08f2f98ad9d15148efe650a17c6b7c2a7ed1ac --- /dev/null +++ b/_keyboardbutton.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram KeyboardButton.""" + +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.types import JSONDict +from telegram._webappinfo import WebAppInfo + +if TYPE_CHECKING: + from telegram import Bot + + +class KeyboardButton(TelegramObject): + """ + This object represents one button of the reply keyboard. At most one of the optional fields + must be used to specify type of the button. For simple text buttons, :obj:`str` + can be used instead of this object to specify text of the button. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`text`, :attr:`request_contact`, :attr:`request_location`, + :attr:`request_poll`, :attr:`web_app`, :attr:`request_users` and :attr:`request_chat` are + equal. + + Note: + * Optional fields are mutually exclusive. + * :attr:`request_contact` and :attr:`request_location` options will only work in Telegram + versions released after 9 April, 2016. Older clients will display unsupported message. + * :attr:`request_poll` option will only work in Telegram versions released after 23 + January, 2020. Older clients will display unsupported message. + * :attr:`web_app` option will only work in Telegram versions released after 16 April, 2022. + Older clients will display unsupported message. + * :attr:`request_users` and :attr:`request_chat` options will only work in Telegram + versions released after 3 February, 2023. Older clients will display unsupported + message. + + .. versionchanged:: 21.0 + Removed deprecated argument and attribute ``request_user``. + .. versionchanged:: 20.0 + :attr:`web_app` is considered as well when comparing objects of this type in terms of + equality. + .. versionchanged:: 20.5 + :attr:`request_users` and :attr:`request_chat` are considered as well when + comparing objects of this type in terms of equality. + + Args: + text (:obj:`str`): Text of the button. If none of the optional fields are used, it will be + sent to the bot as a message when the button is pressed. + request_contact (:obj:`bool`, optional): If :obj:`True`, the user's phone number will be + sent as a contact when the button is pressed. Available in private chats only. + request_location (:obj:`bool`, optional): If :obj:`True`, the user's current location will + be sent when the button is pressed. Available in private chats only. + request_poll (:class:`~telegram.KeyboardButtonPollType`, optional): If specified, the user + will be asked to create a poll and send it to the bot when the button is pressed. + Available in private chats only. + web_app (:class:`~telegram.WebAppInfo`, optional): If specified, the described `Web App + `_ will be launched when the button is pressed. + The Web App will be able to send a :attr:`Message.web_app_data` service message. + Available in private chats only. + + .. versionadded:: 20.0 + + request_users (:class:`KeyboardButtonRequestUsers`, optional): If specified, pressing the + button will open a list of suitable users. Tapping on any user will send its + identifier to the bot in a :attr:`telegram.Message.users_shared` service message. + Available in private chats only. + + .. versionadded:: 20.8 + request_chat (:class:`KeyboardButtonRequestChat`, optional): If specified, pressing the + button will open a list of suitable chats. Tapping on a chat will send its + identifier to the bot in a :attr:`telegram.Message.chat_shared` service message. + Available in private chats only. + + .. versionadded:: 20.1 + Attributes: + text (:obj:`str`): Text of the button. If none of the optional fields are used, it will be + sent to the bot as a message when the button is pressed. + request_contact (:obj:`bool`): Optional. If :obj:`True`, the user's phone number will be + sent as a contact when the button is pressed. Available in private chats only. + request_location (:obj:`bool`): Optional. If :obj:`True`, the user's current location will + be sent when the button is pressed. Available in private chats only. + request_poll (:class:`~telegram.KeyboardButtonPollType`): Optional. If specified, + the user will be asked to create a poll and send it to the bot when the button is + pressed. Available in private chats only. + web_app (:class:`~telegram.WebAppInfo`): Optional. If specified, the described `Web App + `_ will be launched when the button is pressed. + The Web App will be able to send a :attr:`Message.web_app_data` service message. + Available in private chats only. + + .. versionadded:: 20.0 + request_users (:class:`KeyboardButtonRequestUsers`): Optional. If specified, pressing the + button will open a list of suitable users. Tapping on any user will send its + identifier to the bot in a :attr:`telegram.Message.users_shared` service message. + Available in private chats only. + + .. versionadded:: 20.8 + request_chat (:class:`KeyboardButtonRequestChat`): Optional. If specified, pressing the + button will open a list of suitable chats. Tapping on a chat will send its + identifier to the bot in a :attr:`telegram.Message.chat_shared` service message. + Available in private chats only. + + .. versionadded:: 20.1 + """ + + __slots__ = ( + "request_chat", + "request_contact", + "request_location", + "request_poll", + "request_users", + "text", + "web_app", + ) + + def __init__( + self, + text: str, + request_contact: Optional[bool] = None, + request_location: Optional[bool] = None, + request_poll: Optional[KeyboardButtonPollType] = None, + web_app: Optional[WebAppInfo] = None, + request_chat: Optional[KeyboardButtonRequestChat] = None, + request_users: Optional[KeyboardButtonRequestUsers] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + # Required + self.text: str = text + # Optionals + self.request_contact: Optional[bool] = request_contact + self.request_location: Optional[bool] = request_location + self.request_poll: Optional[KeyboardButtonPollType] = request_poll + self.web_app: Optional[WebAppInfo] = web_app + self.request_users: Optional[KeyboardButtonRequestUsers] = request_users + self.request_chat: Optional[KeyboardButtonRequestChat] = request_chat + + self._id_attrs = ( + self.text, + self.request_contact, + self.request_location, + self.request_poll, + self.web_app, + self.request_users, + self.request_chat, + ) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + 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 + if request_user := data.get("request_user"): + api_kwargs = {"request_user": request_user} + + return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs) diff --git a/_keyboardbuttonpolltype.py b/_keyboardbuttonpolltype.py new file mode 100644 index 0000000000000000000000000000000000000000..f3b987a7fc03b68c0fd2a139905e03af53ac6d6d --- /dev/null +++ b/_keyboardbuttonpolltype.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a type of a Telegram Poll.""" +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils import enum +from telegram._utils.types import JSONDict +from telegram.constants import PollType + + +class KeyboardButtonPollType(TelegramObject): + """This object represents type of a poll, which is allowed to be created + and sent when the corresponding button is pressed. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type` is equal. + + Examples: + :any:`Poll Bot ` + + Args: + type (:obj:`str`, optional): If :tg-const:`telegram.Poll.QUIZ` is passed, the user will be + allowed to create only polls in the quiz mode. If :tg-const:`telegram.Poll.REGULAR` is + passed, only regular polls will be allowed. Otherwise, the user will be allowed to + create a poll of any type. + Attributes: + type (:obj:`str`): Optional. If equals :tg-const:`telegram.Poll.QUIZ`, the user will + be allowed to create only polls in the quiz mode. If equals + :tg-const:`telegram.Poll.REGULAR`, only regular polls will be allowed. + Otherwise, the user will be allowed to create a poll of any type. + """ + + __slots__ = ("type",) + + def __init__( + self, + type: Optional[str] = None, # pylint: disable=redefined-builtin + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.type: Optional[str] = enum.get_member(PollType, type, type) + + self._id_attrs = (self.type,) + + self._freeze() diff --git a/_keyboardbuttonrequest.py b/_keyboardbuttonrequest.py new file mode 100644 index 0000000000000000000000000000000000000000..4416952112efce4176454fd7eb54f92053541844 --- /dev/null +++ b/_keyboardbuttonrequest.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains two objects to request chats/users.""" + +from typing import TYPE_CHECKING, Optional + +from telegram._chatadministratorrights import ChatAdministratorRights +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class KeyboardButtonRequestUsers(TelegramObject): + """This object defines the criteria used to request a suitable user. The identifier of the + selected user will be shared with the bot when the corresponding button is pressed. `More + about requesting users » `_. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`request_id` is equal. + + .. versionadded:: 20.8 + This class was previously named ``KeyboardButtonRequestUser``. + + Args: + request_id (:obj:`int`): Signed 32-bit identifier of the request, which will be received + back in the :class:`telegram.UsersShared` object. Must be unique within the message. + user_is_bot (:obj:`bool`, optional): Pass :obj:`True` to request a bot, pass :obj:`False` + to request a regular user. If not specified, no additional restrictions are applied. + user_is_premium (:obj:`bool`, optional): Pass :obj:`True` to request a premium user, pass + :obj:`False` to request a non-premium user. If not specified, no additional + restrictions are applied. + max_quantity (:obj:`int`, optional): The maximum number of users to be selected; + :tg-const:`telegram.constants.KeyboardButtonRequestUsersLimit.MIN_QUANTITY` - + :tg-const:`telegram.constants.KeyboardButtonRequestUsersLimit.MAX_QUANTITY`. + Defaults to :tg-const:`telegram.constants.KeyboardButtonRequestUsersLimit.MIN_QUANTITY` + . + + .. versionadded:: 20.8 + request_name (:obj:`bool`, optional): Pass :obj:`True` to request the users' first and last + name. + + .. versionadded:: 21.1 + request_username (:obj:`bool`, optional): Pass :obj:`True` to request the users' username. + + .. versionadded:: 21.1 + request_photo (:obj:`bool`, optional): Pass :obj:`True` to request the users' photo. + + .. versionadded:: 21.1 + + Attributes: + request_id (:obj:`int`): Identifier of the request. + user_is_bot (:obj:`bool`): Optional. Pass :obj:`True` to request a bot, pass :obj:`False` + to request a regular user. If not specified, no additional restrictions are applied. + user_is_premium (:obj:`bool`): Optional. Pass :obj:`True` to request a premium user, pass + :obj:`False` to request a non-premium user. If not specified, no additional + restrictions are applied. + max_quantity (:obj:`int`): Optional. The maximum number of users to be selected; + :tg-const:`telegram.constants.KeyboardButtonRequestUsersLimit.MIN_QUANTITY` - + :tg-const:`telegram.constants.KeyboardButtonRequestUsersLimit.MAX_QUANTITY`. + Defaults to :tg-const:`telegram.constants.KeyboardButtonRequestUsersLimit.MIN_QUANTITY` + . + + .. versionadded:: 20.8 + request_name (:obj:`bool`): Optional. Pass :obj:`True` to request the users' first and last + name. + + .. versionadded:: 21.1 + request_username (:obj:`bool`): Optional. Pass :obj:`True` to request the users' username. + + .. versionadded:: 21.1 + request_photo (:obj:`bool`): Optional. Pass :obj:`True` to request the users' photo. + + .. versionadded:: 21.1 + + """ + + __slots__ = ( + "max_quantity", + "request_id", + "request_name", + "request_photo", + "request_username", + "user_is_bot", + "user_is_premium", + ) + + def __init__( + self, + request_id: int, + user_is_bot: Optional[bool] = None, + user_is_premium: Optional[bool] = None, + max_quantity: Optional[int] = None, + request_name: Optional[bool] = None, + request_username: Optional[bool] = None, + request_photo: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.request_id: int = request_id + + # Optionals + self.user_is_bot: Optional[bool] = user_is_bot + self.user_is_premium: Optional[bool] = user_is_premium + self.max_quantity: Optional[int] = max_quantity + self.request_name: Optional[bool] = request_name + self.request_username: Optional[bool] = request_username + self.request_photo: Optional[bool] = request_photo + + self._id_attrs = (self.request_id,) + + self._freeze() + + +class KeyboardButtonRequestChat(TelegramObject): + """This object defines the criteria used to request a suitable chat. The identifier of the + selected user will be shared with the bot when the corresponding button is pressed. `More + about requesting users » `_. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`request_id` is equal. + + .. versionadded:: 20.1 + + Args: + request_id (:obj:`int`): Signed 32-bit identifier of the request, which will be received + back in the :class:`telegram.ChatShared` object. Must be unique within the message. + chat_is_channel (:obj:`bool`): Pass :obj:`True` to request a channel chat, pass + :obj:`False` to request a group or a supergroup chat. + chat_is_forum (:obj:`bool`, optional): Pass :obj:`True` to request a forum supergroup, pass + :obj:`False` to request a non-forum chat. If not specified, no additional + restrictions are applied. + chat_has_username (:obj:`bool`, optional): Pass :obj:`True` to request a supergroup or a + channel with a username, pass :obj:`False` to request a chat without a username. If + not specified, no additional restrictions are applied. + chat_is_created (:obj:`bool`, optional): Pass :obj:`True` to request a chat owned by the + user. Otherwise, no additional restrictions are applied. + user_administrator_rights (:class:`ChatAdministratorRights`, optional): Specifies the + required administrator rights of the user in the chat. If not specified, no additional + restrictions are applied. + bot_administrator_rights (:class:`ChatAdministratorRights`, optional): Specifies the + required administrator rights of the bot in the chat. The rights must be a subset of + :paramref:`user_administrator_rights`. If not specified, no additional restrictions are + applied. + bot_is_member (:obj:`bool`, optional): Pass :obj:`True` to request a chat with the bot + as a member. Otherwise, no additional restrictions are applied. + request_title (:obj:`bool`, optional): Pass :obj:`True` to request the chat's title. + + .. versionadded:: 21.1 + request_username (:obj:`bool`, optional): Pass :obj:`True` to request the chat's username. + + .. versionadded:: 21.1 + request_photo (:obj:`bool`, optional): Pass :obj:`True` to request the chat's photo. + + .. versionadded:: 21.1 + Attributes: + request_id (:obj:`int`): Identifier of the request. + chat_is_channel (:obj:`bool`): Pass :obj:`True` to request a channel chat, pass + :obj:`False` to request a group or a supergroup chat. + chat_is_forum (:obj:`bool`): Optional. Pass :obj:`True` to request a forum supergroup, pass + :obj:`False` to request a non-forum chat. If not specified, no additional + restrictions are applied. + chat_has_username (:obj:`bool`): Optional. Pass :obj:`True` to request a supergroup or a + channel with a username, pass :obj:`False` to request a chat without a username. If + not specified, no additional restrictions are applied. + chat_is_created (:obj:`bool`) Optional. Pass :obj:`True` to request a chat owned by the + user. Otherwise, no additional restrictions are applied. + user_administrator_rights (:class:`ChatAdministratorRights`) Optional. Specifies the + required administrator rights of the user in the chat. If not specified, no additional + restrictions are applied. + bot_administrator_rights (:class:`ChatAdministratorRights`) Optional. Specifies the + required administrator rights of the bot in the chat. The rights must be a subset of + :attr:`user_administrator_rights`. If not specified, no additional restrictions are + applied. + bot_is_member (:obj:`bool`) Optional. Pass :obj:`True` to request a chat with the bot + as a member. Otherwise, no additional restrictions are applied. + request_title (:obj:`bool`): Optional. Pass :obj:`True` to request the chat's title. + + .. versionadded:: 21.1 + request_username (:obj:`bool`): Optional. Pass :obj:`True` to request the chat's username. + + .. versionadded:: 21.1 + request_photo (:obj:`bool`): Optional. Pass :obj:`True` to request the chat's photo. + + .. versionadded:: 21.1 + """ + + __slots__ = ( + "bot_administrator_rights", + "bot_is_member", + "chat_has_username", + "chat_is_channel", + "chat_is_created", + "chat_is_forum", + "request_id", + "request_photo", + "request_title", + "request_username", + "user_administrator_rights", + ) + + def __init__( + self, + request_id: int, + chat_is_channel: bool, + chat_is_forum: Optional[bool] = None, + chat_has_username: Optional[bool] = None, + chat_is_created: Optional[bool] = None, + user_administrator_rights: Optional[ChatAdministratorRights] = None, + bot_administrator_rights: Optional[ChatAdministratorRights] = None, + bot_is_member: Optional[bool] = None, + request_title: Optional[bool] = None, + request_username: Optional[bool] = None, + request_photo: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # required + self.request_id: int = request_id + self.chat_is_channel: bool = chat_is_channel + + # optional + self.chat_is_forum: Optional[bool] = chat_is_forum + self.chat_has_username: Optional[bool] = chat_has_username + self.chat_is_created: Optional[bool] = chat_is_created + self.user_administrator_rights: Optional[ChatAdministratorRights] = ( + user_administrator_rights + ) + self.bot_administrator_rights: Optional[ChatAdministratorRights] = bot_administrator_rights + self.bot_is_member: Optional[bool] = bot_is_member + self.request_title: Optional[bool] = request_title + self.request_username: Optional[bool] = request_username + self.request_photo: Optional[bool] = request_photo + + self._id_attrs = (self.request_id,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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["bot_administrator_rights"] = ChatAdministratorRights.de_json( + data.get("bot_administrator_rights"), bot + ) + + return super().de_json(data=data, bot=bot) diff --git a/_linkpreviewoptions.py b/_linkpreviewoptions.py new file mode 100644 index 0000000000000000000000000000000000000000..b88fbc55877fd48f28619c9136404c4d949eccec --- /dev/null +++ b/_linkpreviewoptions.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the LinkPreviewOptions class.""" + + +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput + + +class LinkPreviewOptions(TelegramObject): + """ + Describes the options used for link preview generation. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`is_disabled`, :attr:`url`, :attr:`prefer_small_media`, + :attr:`prefer_large_media`, and :attr:`show_above_text` are equal. + + .. versionadded:: 20.8 + + Args: + is_disabled (:obj:`bool`, optional): :obj:`True`, if the link preview is disabled. + url (:obj:`str`, optional): The URL to use for the link preview. If empty, then the first + URL found in the message text will be used. + prefer_small_media (:obj:`bool`, optional): :obj:`True`, if the media in the link preview + is supposed to be shrunk; ignored if the URL isn't explicitly specified or media size + change isn't supported for the preview. + prefer_large_media (:obj:`bool`, optional): :obj:`True`, if the media in the link preview + is supposed to be enlarged; ignored if the URL isn't explicitly specified or media + size change isn't supported for the preview. + show_above_text (:obj:`bool`, optional): :obj:`True`, if the link preview must be shown + above the message text; otherwise, the link preview will be shown below the message + text. + + Attributes: + is_disabled (:obj:`bool`): Optional. :obj:`True`, if the link preview is disabled. + url (:obj:`str`): Optional. The URL to use for the link preview. If empty, then the first + URL found in the message text will be used. + prefer_small_media (:obj:`bool`): Optional. :obj:`True`, if the media in the link preview + is supposed to be shrunk; ignored if the URL isn't explicitly specified or media size + change isn't supported for the preview. + prefer_large_media (:obj:`bool`): Optional. :obj:`True`, if the media in the link preview + is supposed to be enlarged; ignored if the URL isn't explicitly specified or media size + change isn't supported for the preview. + show_above_text (:obj:`bool`): Optional. :obj:`True`, if the link preview must be shown + above the message text; otherwise, the link preview will be shown below the message + text. + """ + + __slots__ = ( + "is_disabled", + "prefer_large_media", + "prefer_small_media", + "show_above_text", + "url", + ) + + def __init__( + self, + is_disabled: ODVInput[bool] = DEFAULT_NONE, + url: ODVInput[str] = DEFAULT_NONE, + prefer_small_media: ODVInput[bool] = DEFAULT_NONE, + prefer_large_media: ODVInput[bool] = DEFAULT_NONE, + show_above_text: ODVInput[bool] = DEFAULT_NONE, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + # Optionals + + self.is_disabled: ODVInput[bool] = is_disabled + self.url: ODVInput[str] = url + self.prefer_small_media: ODVInput[bool] = prefer_small_media + self.prefer_large_media: ODVInput[bool] = prefer_large_media + self.show_above_text: ODVInput[bool] = show_above_text + + self._id_attrs = ( + self.is_disabled, + self.url, + self.prefer_small_media, + self.prefer_large_media, + self.show_above_text, + ) + self._freeze() diff --git a/_loginurl.py b/_loginurl.py new file mode 100644 index 0000000000000000000000000000000000000000..4201b7ab50faa3b75e62c1a0473ce9be6f6b76f2 --- /dev/null +++ b/_loginurl.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram LoginUrl.""" +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class LoginUrl(TelegramObject): + """This object represents a parameter of the inline keyboard button used to automatically + authorize a user. Serves as a great replacement for the Telegram Login Widget when the user is + coming from Telegram. All the user needs to do is tap/click a button and confirm that they want + to log in. Telegram apps support these buttons as of version 5.7. + + Sample bot: `@discussbot `_ + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`url` is equal. + + Note: + You must always check the hash of the received data to verify the authentication + and the integrity of the data as described in + `Checking authorization `_ + + Args: + url (:obj:`str`): An HTTPS URL to be opened with user authorization data added to the query + string when the button is pressed. If the user refuses to provide authorization data, + the original URL without information about the user will be opened. The data added is + the same as described in + `Receiving authorization data + `_. + forward_text (:obj:`str`, optional): New text of the button in forwarded messages. + bot_username (:obj:`str`, optional): Username of a bot, which will be used for user + authorization. See + `Setting up a bot `_ + for more details. If not specified, the current + bot's username will be assumed. The url's domain must be the same as the domain linked + with the bot. See + `Linking your domain to the bot + `_ + for more details. + request_write_access (:obj:`bool`, optional): Pass :obj:`True` to request the permission + for your bot to send messages to the user. + + Attributes: + url (:obj:`str`): An HTTPS URL to be opened with user authorization data added to the query + string when the button is pressed. If the user refuses to provide authorization data, + the original URL without information about the user will be opened. The data added is + the same as described in + `Receiving authorization data + `_. + forward_text (:obj:`str`): Optional. New text of the button in forwarded messages. + bot_username (:obj:`str`): Optional. Username of a bot, which will be used for user + authorization. See + `Setting up a bot `_ + for more details. If not specified, the current + bot's username will be assumed. The url's domain must be the same as the domain linked + with the bot. See + `Linking your domain to the bot + `_ + for more details. + request_write_access (:obj:`bool`): Optional. Pass :obj:`True` to request the permission + for your bot to send messages to the user. + + """ + + __slots__ = ("bot_username", "forward_text", "request_write_access", "url") + + def __init__( + self, + url: str, + forward_text: Optional[str] = None, + bot_username: Optional[str] = None, + request_write_access: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.url: str = url + # Optional + self.forward_text: Optional[str] = forward_text + self.bot_username: Optional[str] = bot_username + self.request_write_access: Optional[bool] = request_write_access + + self._id_attrs = (self.url,) + + self._freeze() diff --git a/_menubutton.py b/_menubutton.py new file mode 100644 index 0000000000000000000000000000000000000000..50b6511b08df7db86c40d6f56db2568345328fe2 --- /dev/null +++ b/_menubutton.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains objects related to Telegram menu buttons.""" +from typing import TYPE_CHECKING, Dict, Final, Optional, Type + +from telegram import constants +from telegram._telegramobject import TelegramObject +from telegram._utils import enum +from telegram._utils.types import JSONDict +from telegram._webappinfo import WebAppInfo + +if TYPE_CHECKING: + from telegram import Bot + + +class MenuButton(TelegramObject): + """This object describes the bot's menu button in a private chat. It should be one of + + * :class:`telegram.MenuButtonCommands` + * :class:`telegram.MenuButtonWebApp` + * :class:`telegram.MenuButtonDefault` + + If a menu button other than :class:`telegram.MenuButtonDefault` is set for a private chat, + then it is applied in the chat. Otherwise the default menu button is applied. By default, the + menu button opens the list of bot commands. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type` is equal. For subclasses with additional attributes, + the notion of equality is overridden. + + .. versionadded:: 20.0 + + Args: + type (:obj:`str`): Type of menu button that the instance represents. + + Attributes: + type (:obj:`str`): Type of menu button that the instance represents. + """ + + __slots__ = ("type",) + + def __init__( + self, + type: str, + *, + api_kwargs: Optional[JSONDict] = None, + ): # pylint: disable=redefined-builtin + super().__init__(api_kwargs=api_kwargs) + self.type: str = enum.get_member(constants.MenuButtonType, type, type) + + self._id_attrs = (self.type,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["MenuButton"]: + """Converts JSON data to the appropriate :class:`MenuButton` object, i.e. takes + care of selecting the correct subclass. + + Args: + data (Dict[:obj:`str`, ...]): The JSON data. + bot (:class:`telegram.Bot`, optional): The bot associated with this object. Defaults to + :obj:`None`, in which case shortcut methods will not be available. + + .. versionchanged:: 21.4 + :paramref:`bot` is now optional and defaults to :obj:`None` + + Returns: + The Telegram object. + + """ + 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, + cls.DEFAULT: MenuButtonDefault, + } + + if cls is MenuButton and data.get("type") in _class_mapping: + return _class_mapping[data.pop("type")].de_json(data, bot=bot) + return super().de_json(data=data, bot=bot) + + COMMANDS: Final[str] = constants.MenuButtonType.COMMANDS + """:const:`telegram.constants.MenuButtonType.COMMANDS`""" + WEB_APP: Final[str] = constants.MenuButtonType.WEB_APP + """:const:`telegram.constants.MenuButtonType.WEB_APP`""" + DEFAULT: Final[str] = constants.MenuButtonType.DEFAULT + """:const:`telegram.constants.MenuButtonType.DEFAULT`""" + + +class MenuButtonCommands(MenuButton): + """Represents a menu button, which opens the bot's list of commands. + + .. include:: inclusions/menu_button_command_video.rst + + .. versionadded:: 20.0 + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.MenuButtonType.COMMANDS`. + """ + + __slots__ = () + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(type=constants.MenuButtonType.COMMANDS, api_kwargs=api_kwargs) + self._freeze() + + +class MenuButtonWebApp(MenuButton): + """Represents a menu button, which launches a + `Web App `_. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type`, :attr:`text` and :attr:`web_app` + are equal. + + .. versionadded:: 20.0 + + Args: + text (:obj:`str`): Text of the button. + web_app (:class:`telegram.WebAppInfo`): Description of the Web App that will be launched + when the user presses the button. The Web App will be able to send an arbitrary + message on behalf of the user using the method :meth:`~telegram.Bot.answerWebAppQuery` + of :class:`~telegram.Bot`. Alternatively, a ``t.me`` link to a Web App of the bot can + be specified in the object instead of the Web App's URL, in which case the Web App + will be opened as if the user pressed the link. + + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.MenuButtonType.WEB_APP`. + text (:obj:`str`): Text of the button. + web_app (:class:`telegram.WebAppInfo`): Description of the Web App that will be launched + when the user presses the button. The Web App will be able to send an arbitrary + message on behalf of the user using the method :meth:`~telegram.Bot.answerWebAppQuery` + of :class:`~telegram.Bot`. Alternatively, a ``t.me`` link to a Web App of the bot can + be specified in the object instead of the Web App's URL, in which case the Web App + will be opened as if the user pressed the link. + """ + + __slots__ = ("text", "web_app") + + def __init__(self, text: str, web_app: WebAppInfo, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(type=constants.MenuButtonType.WEB_APP, api_kwargs=api_kwargs) + with self._unfrozen(): + self.text: str = text + self.web_app: WebAppInfo = web_app + + self._id_attrs = (self.type, self.text, self.web_app) + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) # type: ignore[return-value] + + +class MenuButtonDefault(MenuButton): + """Describes that no specific value for the menu button was set. + + .. versionadded:: 20.0 + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.MenuButtonType.DEFAULT`. + """ + + __slots__ = () + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(type=constants.MenuButtonType.DEFAULT, api_kwargs=api_kwargs) + self._freeze() diff --git a/_message.py b/_message.py new file mode 100644 index 0000000000000000000000000000000000000000..11bee5724939dff60d052afa11c20126e4759bfb --- /dev/null +++ b/_message.py @@ -0,0 +1,5037 @@ +#!/usr/bin/env python +# pylint: disable=too-many-instance-attributes, too-many-arguments +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram Message.""" + +import datetime +import re +from html import escape +from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Tuple, TypedDict, Union + +from telegram._chat import Chat +from telegram._chatbackground import ChatBackground +from telegram._chatboost import ChatBoostAdded +from telegram._dice import Dice +from telegram._files.animation import Animation +from telegram._files.audio import Audio +from telegram._files.contact import Contact +from telegram._files.document import Document +from telegram._files.location import Location +from telegram._files.photosize import PhotoSize +from telegram._files.sticker import Sticker +from telegram._files.venue import Venue +from telegram._files.video import Video +from telegram._files.videonote import VideoNote +from telegram._files.voice import Voice +from telegram._forumtopic import ( + ForumTopicClosed, + ForumTopicCreated, + ForumTopicEdited, + ForumTopicReopened, + GeneralForumTopicHidden, + GeneralForumTopicUnhidden, +) +from telegram._games.game import Game +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._linkpreviewoptions import LinkPreviewOptions +from telegram._messageautodeletetimerchanged import MessageAutoDeleteTimerChanged +from telegram._messageentity import MessageEntity +from telegram._paidmedia import PaidMediaInfo +from telegram._passport.passportdata import PassportData +from telegram._payment.invoice import Invoice +from telegram._payment.refundedpayment import RefundedPayment +from telegram._payment.successfulpayment import SuccessfulPayment +from telegram._poll import Poll +from telegram._proximityalerttriggered import ProximityAlertTriggered +from telegram._reply import ReplyParameters +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.datetime import extract_tzinfo_from_defaults, from_timestamp +from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue +from telegram._utils.entities import parse_message_entities, parse_message_entity +from telegram._utils.strings import TextEncoding +from telegram._utils.types import ( + CorrectOptionID, + FileInput, + JSONDict, + MarkdownVersion, + ODVInput, + ReplyMarkup, +) +from telegram._utils.warnings import warn +from telegram._videochat import ( + VideoChatEnded, + VideoChatParticipantsInvited, + VideoChatScheduled, + VideoChatStarted, +) +from telegram._webappdata import WebAppData +from telegram._writeaccessallowed import WriteAccessAllowed +from telegram.constants import ZERO_DATE, MessageAttachmentType, ParseMode +from telegram.helpers import escape_markdown +from telegram.warnings import PTBDeprecationWarning + +if TYPE_CHECKING: + from telegram import ( + Bot, + ExternalReplyInfo, + GameHighScore, + Giveaway, + GiveawayCompleted, + GiveawayCreated, + GiveawayWinners, + InputMedia, + InputMediaAudio, + InputMediaDocument, + InputMediaPhoto, + InputMediaVideo, + InputPollOption, + LabeledPrice, + MessageId, + MessageOrigin, + ReactionType, + TextQuote, + ) + + +class _ReplyKwargs(TypedDict): + __slots__ = ("chat_id", "reply_parameters") # type: ignore[misc] + + chat_id: Union[str, int] + reply_parameters: ReplyParameters + + +class MaybeInaccessibleMessage(TelegramObject): + """Base class for Telegram Message Objects. + + Currently, that includes :class:`telegram.Message` and :class:`telegram.InaccessibleMessage`. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`message_id` and :attr:`chat` are equal + + .. versionchanged:: 21.0 + ``__bool__`` is no longer overriden and defaults to Pythons standard implementation. + + .. versionadded:: 20.8 + + Args: + message_id (:obj:`int`): Unique message identifier. + date (:class:`datetime.datetime`): Date the message was sent in Unix time or 0 in Unix + time. Converted to :class:`datetime.datetime` + + |datetime_localization| + chat (:class:`telegram.Chat`): Conversation the message belongs to. + + Attributes: + message_id (:obj:`int`): Unique message identifier. + date (:class:`datetime.datetime`): Date the message was sent in Unix time or 0 in Unix + time. Converted to :class:`datetime.datetime` + + |datetime_localization| + chat (:class:`telegram.Chat`): Conversation the message belongs to. + """ + + __slots__ = ("chat", "date", "message_id") + + def __init__( + self, + chat: Chat, + message_id: int, + date: datetime.datetime, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.chat: Chat = chat + self.message_id: int = message_id + self.date: datetime.datetime = date + + self._id_attrs = (self.message_id, self.chat) + + self._freeze() + + @property + def is_accessible(self) -> bool: + """Convenience attribute. :obj:`True`, if the date is not 0 in Unix time. + + .. versionadded:: 20.8 + """ + # Once we drop support for python 3.9, this can be made a TypeGuard function: + # def is_accessible(self) -> TypeGuard[Message]: + return self.date != ZERO_DATE + + @classmethod + def _de_json( + cls, + data: Optional[JSONDict], + bot: Optional["Bot"] = None, + api_kwargs: Optional[JSONDict] = None, + ) -> Optional["MaybeInaccessibleMessage"]: + """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) + return Message.de_json(data=data, bot=bot) + + # Get the local timezone from the bot if it has defaults + loc_tzinfo = extract_tzinfo_from_defaults(bot) + + # this is to include the Literal from InaccessibleMessage + if data["date"] == 0: + data["date"] = ZERO_DATE + else: + data["date"] = from_timestamp(data["date"], tzinfo=loc_tzinfo) + + data["chat"] = Chat.de_json(data.get("chat"), bot) + return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs) + + +class InaccessibleMessage(MaybeInaccessibleMessage): + """This object represents an inaccessible message. + + These are messages that are e.g. deleted. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`message_id` and :attr:`chat` are equal + + .. versionadded:: 20.8 + + Args: + message_id (:obj:`int`): Unique message identifier. + chat (:class:`telegram.Chat`): Chat the message belongs to. + + Attributes: + message_id (:obj:`int`): Unique message identifier. + date (:class:`constants.ZERO_DATE`): Always :tg-const:`telegram.constants.ZERO_DATE`. + The field can be used to differentiate regular and inaccessible messages. + chat (:class:`telegram.Chat`): Chat the message belongs to. + """ + + __slots__ = () + + def __init__( + self, + chat: Chat, + message_id: int, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(chat=chat, message_id=message_id, date=ZERO_DATE, api_kwargs=api_kwargs) + self._freeze() + + +class Message(MaybeInaccessibleMessage): + # fmt: off + """This object represents a message. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`message_id` and :attr:`chat` are equal. + + Note: + In Python :keyword:`from` is a reserved word. Use :paramref:`from_user` instead. + + .. versionchanged:: 21.0 + Removed deprecated arguments and attributes ``user_shared``, ``forward_from``, + ``forward_from_chat``, ``forward_from_message_id``, ``forward_signature``, + ``forward_sender_name`` and ``forward_date``. + + .. versionchanged:: 20.8 + * This class is now a subclass of :class:`telegram.MaybeInaccessibleMessage`. + * The :paramref:`pinned_message` now can be either :class:`telegram.Message` or + :class:`telegram.InaccessibleMessage`. + + .. versionchanged:: 20.0 + + * The arguments and attributes ``voice_chat_scheduled``, ``voice_chat_started`` and + ``voice_chat_ended``, ``voice_chat_participants_invited`` were renamed to + :paramref:`video_chat_scheduled`/:attr:`video_chat_scheduled`, + :paramref:`video_chat_started`/:attr:`video_chat_started`, + :paramref:`video_chat_ended`/:attr:`video_chat_ended` and + :paramref:`video_chat_participants_invited`/:attr:`video_chat_participants_invited`, + respectively, in accordance to Bot API 6.0. + * The following are now keyword-only arguments in Bot methods: + ``{read, write, connect, pool}_timeout``, ``api_kwargs``, ``contact``, ``quote``, + ``filename``, ``loaction``, ``venue``. Use a named argument for those, + and notice that some positional arguments changed position as a result. + + Args: + message_id (:obj:`int`): Unique message identifier inside this chat. + from_user (:class:`telegram.User`, optional): Sender of the message; may be empty for + messages sent to channels. For backward compatibility, if the message was sent on + behalf of a chat, the field contains a fake sender user in non-channel chats. + sender_chat (:class:`telegram.Chat`, optional): Sender of the message when sent on behalf + of a chat. For example, the supergroup itself for messages sent by its anonymous + administrators or a linked channel for messages automatically forwarded to the + channel's discussion group. For backward compatibility, if the message was sent on + behalf of a chat, the field from contains a fake sender user in non-channel chats. + date (:class:`datetime.datetime`): Date the message was sent in Unix time. Converted to + :class:`datetime.datetime`. + + .. versionchanged:: 20.3 + |datetime_localization| + chat (:class:`telegram.Chat`): Conversation the message belongs to. + is_automatic_forward (:obj:`bool`, optional): :obj:`True`, if the message is a channel + post that was automatically forwarded to the connected discussion group. + + .. versionadded:: 13.9 + reply_to_message (:class:`telegram.Message`, optional): For replies, the original message. + Note that the Message object in this field will not contain further + ``reply_to_message`` fields even if it itself is a reply. + edit_date (:class:`datetime.datetime`, optional): Date the message was last edited in Unix + time. Converted to :class:`datetime.datetime`. + + .. versionchanged:: 20.3 + |datetime_localization| + has_protected_content (:obj:`bool`, optional): :obj:`True`, if the message can't be + forwarded. + + .. versionadded:: 13.9 + is_from_offline (:obj:`bool`, optional): :obj:`True`, if the message was sent + by an implicit action, for example, as an away or a greeting business message, + or as a scheduled message. + + .. versionadded:: 21.1 + media_group_id (:obj:`str`, optional): The unique identifier of a media message group this + message belongs to. + text (:obj:`str`, optional): For text messages, the actual UTF-8 text of the message, + 0-:tg-const:`telegram.constants.MessageLimit.MAX_TEXT_LENGTH` characters. + entities (Sequence[:class:`telegram.MessageEntity`], optional): For text messages, special + entities like usernames, URLs, bot commands, etc. that appear in the text. See + :attr:`parse_entity` and :attr:`parse_entities` methods for how to use properly. + This list is empty if the message does not contain entities. + + .. versionchanged:: 20.0 + |sequenceclassargs| + + link_preview_options (:class:`telegram.LinkPreviewOptions`, optional): Options used for + link preview generation for the message, if it is a text message and link preview + options were changed. + + .. versionadded:: 20.8 + + effect_id (:obj:`str`, optional): Unique identifier of the message effect added to the + message. + + .. versionadded:: 21.3 + + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): For messages with a + Caption. Special entities like usernames, URLs, bot commands, etc. that appear in the + caption. See :attr:`Message.parse_caption_entity` and :attr:`parse_caption_entities` + methods for how to use properly. This list is empty if the message does not contain + caption entities. + + .. versionchanged:: 20.0 + |sequenceclassargs| + + show_caption_above_media (:obj:`bool`, optional): |show_cap_above_med| + + .. versionadded:: 21.3 + audio (:class:`telegram.Audio`, optional): Message is an audio file, information + about the file. + document (:class:`telegram.Document`, optional): Message is a general file, information + about the file. + animation (:class:`telegram.Animation`, optional): Message is an animation, information + about the animation. For backward compatibility, when this field is set, the document + field will also be set. + game (:class:`telegram.Game`, optional): Message is a game, information about the game. + :ref:`More about games >> `. + photo (Sequence[:class:`telegram.PhotoSize`], optional): Message is a photo, available + sizes of the photo. This list is empty if the message does not contain a photo. + + .. versionchanged:: 20.0 + |sequenceclassargs| + + sticker (:class:`telegram.Sticker`, optional): Message is a sticker, information + about the sticker. + story (:class:`telegram.Story`, optional): Message is a forwarded story. + + .. versionadded:: 20.5 + video (:class:`telegram.Video`, optional): Message is a video, information about the + video. + voice (:class:`telegram.Voice`, optional): Message is a voice message, information about + the file. + video_note (:class:`telegram.VideoNote`, optional): Message is a + `video note `_, information + about the video message. + new_chat_members (Sequence[:class:`telegram.User`], optional): New members that were added + to the group or supergroup and information about them (the bot itself may be one of + these members). This list is empty if the message does not contain new chat members. + + .. versionchanged:: 20.0 + |sequenceclassargs| + + caption (:obj:`str`, optional): Caption for the animation, audio, document, paid media, + photo, video + or voice, 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters. + contact (:class:`telegram.Contact`, optional): Message is a shared contact, information + about the contact. + location (:class:`telegram.Location`, optional): Message is a shared location, information + about the location. + venue (:class:`telegram.Venue`, optional): Message is a venue, information about the + venue. For backward compatibility, when this field is set, the location field will + also be set. + left_chat_member (:class:`telegram.User`, optional): A member was removed from the group, + information about them (this member may be the bot itself). + new_chat_title (:obj:`str`, optional): A chat title was changed to this value. + new_chat_photo (Sequence[:class:`telegram.PhotoSize`], optional): A chat photo was changed + to this value. This list is empty if the message does not contain a new chat photo. + + .. versionchanged:: 20.0 + |sequenceclassargs| + + delete_chat_photo (:obj:`bool`, optional): Service message: The chat photo was deleted. + group_chat_created (:obj:`bool`, optional): Service message: The group has been created. + supergroup_chat_created (:obj:`bool`, optional): Service message: The supergroup has been + created. This field can't be received in a message coming through updates, because bot + can't be a member of a supergroup when it is created. It can only be found in + :attr:`reply_to_message` if someone replies to a very first message in a directly + created supergroup. + channel_chat_created (:obj:`bool`, optional): Service message: The channel has been + created. This field can't be received in a message coming through updates, because bot + can't be a member of a channel when it is created. It can only be found in + :attr:`reply_to_message` if someone replies to a very first message in a channel. + message_auto_delete_timer_changed (:class:`telegram.MessageAutoDeleteTimerChanged`, \ + optional): Service message: auto-delete timer settings changed in the chat. + + .. versionadded:: 13.4 + migrate_to_chat_id (:obj:`int`, optional): The group has been migrated to a supergroup + with the specified identifier. + migrate_from_chat_id (:obj:`int`, optional): The supergroup has been migrated from a group + with the specified identifier. + pinned_message (:class:`telegram.MaybeInaccessibleMessage`, optional): Specified message + was pinned. Note that the Message object in this field will not contain further + :attr:`reply_to_message` fields even if it is itself a reply. + + .. versionchanged:: 20.8 + This attribute now is either :class:`telegram.Message` or + :class:`telegram.InaccessibleMessage`. + invoice (:class:`telegram.Invoice`, optional): Message is an invoice for a payment, + information about the invoice. + :ref:`More about payments >> `. + successful_payment (:class:`telegram.SuccessfulPayment`, optional): Message is a service + message about a successful payment, information about the payment. + :ref:`More about payments >> `. + connected_website (:obj:`str`, optional): The domain name of the website on which the user + has logged in. + `More about Telegram Login >> `_. + author_signature (:obj:`str`, optional): Signature of the post author for messages in + channels, or the custom title of an anonymous group administrator. + passport_data (:class:`telegram.PassportData`, optional): Telegram Passport data. + poll (:class:`telegram.Poll`, optional): Message is a native poll, + information about the poll. + dice (:class:`telegram.Dice`, optional): Message is a dice with random value. + via_bot (:class:`telegram.User`, optional): Bot through which message was sent. + proximity_alert_triggered (:class:`telegram.ProximityAlertTriggered`, optional): Service + message. A user in the chat triggered another user's proximity alert while sharing + Live Location. + video_chat_scheduled (:class:`telegram.VideoChatScheduled`, optional): Service message: + video chat scheduled. + + .. versionadded:: 20.0 + video_chat_started (:class:`telegram.VideoChatStarted`, optional): Service message: video + chat started. + + .. versionadded:: 20.0 + video_chat_ended (:class:`telegram.VideoChatEnded`, optional): Service message: video chat + ended. + + .. versionadded:: 20.0 + video_chat_participants_invited (:class:`telegram.VideoChatParticipantsInvited` optional): + Service message: new participants invited to a video chat. + + .. versionadded:: 20.0 + web_app_data (:class:`telegram.WebAppData`, optional): Service message: data sent by a Web + App. + + .. versionadded:: 20.0 + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. :paramref:`~telegram.InlineKeyboardButton.login_url` buttons are + represented as ordinary url buttons. + is_topic_message (:obj:`bool`, optional): :obj:`True`, if the message is sent to a forum + topic. + + .. versionadded:: 20.0 + message_thread_id (:obj:`int`, optional): Unique identifier of a message thread to which + the message belongs; for supergroups only. + + .. versionadded:: 20.0 + forum_topic_created (:class:`telegram.ForumTopicCreated`, optional): Service message: + forum topic created. + + .. versionadded:: 20.0 + forum_topic_closed (:class:`telegram.ForumTopicClosed`, optional): Service message: + forum topic closed. + + .. versionadded:: 20.0 + forum_topic_reopened (:class:`telegram.ForumTopicReopened`, optional): Service message: + forum topic reopened. + + .. versionadded:: 20.0 + forum_topic_edited (:class:`telegram.ForumTopicEdited`, optional): Service message: + forum topic edited. + + .. versionadded:: 20.0 + general_forum_topic_hidden (:class:`telegram.GeneralForumTopicHidden`, optional): + Service message: General forum topic hidden. + + .. versionadded:: 20.0 + general_forum_topic_unhidden (:class:`telegram.GeneralForumTopicUnhidden`, optional): + Service message: General forum topic unhidden. + + .. versionadded:: 20.0 + write_access_allowed (:class:`telegram.WriteAccessAllowed`, optional): Service message: + the user allowed the bot to write messages after adding it to the attachment or side + menu, launching a Web App from a link, or accepting an explicit request from a Web App + sent by the method + `requestWriteAccess `_. + + .. versionadded:: 20.0 + has_media_spoiler (:obj:`bool`, optional): :obj:`True`, if the message media is covered + by a spoiler animation. + + .. versionadded:: 20.0 + users_shared (:class:`telegram.UsersShared`, optional): Service message: users were shared + with the bot + + .. versionadded:: 20.8 + chat_shared (:class:`telegram.ChatShared`, optional):Service message: a chat was shared + with the bot. + + .. versionadded:: 20.1 + giveaway_created (:class:`telegram.GiveawayCreated`, optional): Service message: a + scheduled giveaway was created + + .. versionadded:: 20.8 + giveaway (:class:`telegram.Giveaway`, optional): The message is a scheduled giveaway + message + + .. versionadded:: 20.8 + giveaway_winners (:class:`telegram.GiveawayWinners`, optional): A giveaway with public + winners was completed + + .. versionadded:: 20.8 + giveaway_completed (:class:`telegram.GiveawayCompleted`, optional): Service message: a + giveaway without public winners was completed + + .. versionadded:: 20.8 + external_reply (:class:`telegram.ExternalReplyInfo`, optional): Information about the + message that is being replied to, which may come from another chat or forum topic. + + .. versionadded:: 20.8 + quote (:class:`telegram.TextQuote`, optional): For replies that quote part of the original + message, the quoted part of the message. + + .. versionadded:: 20.8 + forward_origin (:class:`telegram.MessageOrigin`, optional): Information about the original + message for forwarded messages + + .. versionadded:: 20.8 + reply_to_story (:class:`telegram.Story`, optional): For replies to a story, the original + story. + + .. versionadded:: 21.0 + boost_added (:class:`telegram.ChatBoostAdded`, optional): Service message: user boosted + the chat. + + .. versionadded:: 21.0 + sender_boost_count (:obj:`int`, optional): If the sender of the + message boosted the chat, the number of boosts added by the user. + + .. versionadded:: 21.0 + business_connection_id (:obj:`str`, optional): Unique identifier of the business connection + from which the message was received. If non-empty, the message belongs to a chat of the + corresponding business account that is independent from any potential bot chat which + might share the same identifier. + + .. versionadded:: 21.1 + + sender_business_bot (:class:`telegram.User`, optional): The bot that actually sent the + message on behalf of the business account. Available only for outgoing messages sent + on behalf of the connected business account. + + .. versionadded:: 21.1 + + chat_background_set (:class:`telegram.ChatBackground`, optional): Service message: chat + background set. + + .. versionadded:: 21.2 + paid_media (:class:`telegram.PaidMediaInfo`, optional): Message contains paid media; + information about the paid media. + + .. versionadded:: 21.4 + refunded_payment (:class:`telegram.RefundedPayment`, optional): Message is a service + message about a refunded payment, information about the payment. + + .. versionadded:: 21.4 + + Attributes: + message_id (:obj:`int`): Unique message identifier inside this chat. + from_user (:class:`telegram.User`): Optional. Sender of the message; may be empty for + messages sent to channels. For backward compatibility, if the message was sent on + behalf of a chat, the field contains a fake sender user in non-channel chats. + sender_chat (:class:`telegram.Chat`): Optional. Sender of the message when sent on behalf + of a chat. For example, the supergroup itself for messages sent by its anonymous + administrators or a linked channel for messages automatically forwarded to the + channel's discussion group. For backward compatibility, if the message was sent on + behalf of a chat, the field from contains a fake sender user in non-channel chats. + date (:class:`datetime.datetime`): Date the message was sent in Unix time. Converted to + :class:`datetime.datetime`. + + .. versionchanged:: 20.3 + |datetime_localization| + chat (:class:`telegram.Chat`): Conversation the message belongs to. + is_automatic_forward (:obj:`bool`): Optional. :obj:`True`, if the message is a channel + post that was automatically forwarded to the connected discussion group. + + .. versionadded:: 13.9 + reply_to_message (:class:`telegram.Message`): Optional. For replies, the original message. + Note that the Message object in this field will not contain further + ``reply_to_message`` fields even if it itself is a reply. + edit_date (:class:`datetime.datetime`): Optional. Date the message was last edited in Unix + time. Converted to :class:`datetime.datetime`. + + .. versionchanged:: 20.3 + |datetime_localization| + has_protected_content (:obj:`bool`): Optional. :obj:`True`, if the message can't be + forwarded. + + .. versionadded:: 13.9 + is_from_offline (:obj:`bool`): Optional. :obj:`True`, if the message was sent + by an implicit action, for example, as an away or a greeting business message, + or as a scheduled message. + + .. versionadded:: 21.1 + media_group_id (:obj:`str`): Optional. The unique identifier of a media message group this + message belongs to. + text (:obj:`str`): Optional. For text messages, the actual UTF-8 text of the message, + 0-:tg-const:`telegram.constants.MessageLimit.MAX_TEXT_LENGTH` characters. + entities (Tuple[:class:`telegram.MessageEntity`]): Optional. For text messages, special + entities like usernames, URLs, bot commands, etc. that appear in the text. See + :attr:`parse_entity` and :attr:`parse_entities` methods for how to use properly. + This list is empty if the message does not contain entities. + + .. versionchanged:: 20.0 + |tupleclassattrs| + + link_preview_options (:class:`telegram.LinkPreviewOptions`): Optional. Options used for + link preview generation for the message, if it is a text message and link preview + options were changed. + + .. versionadded:: 20.8 + + effect_id (:obj:`str`): Optional. Unique identifier of the message effect added to the + message. + + ..versionadded:: 21.3 + + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. For messages with a + Caption. Special entities like usernames, URLs, bot commands, etc. that appear in the + caption. See :attr:`Message.parse_caption_entity` and :attr:`parse_caption_entities` + methods for how to use properly. This list is empty if the message does not contain + caption entities. + + .. versionchanged:: 20.0 + |tupleclassattrs| + + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: 21.3 + audio (:class:`telegram.Audio`): Optional. Message is an audio file, information + about the file. + + .. seealso:: :wiki:`Working with Files and Media ` + document (:class:`telegram.Document`): Optional. Message is a general file, information + about the file. + + .. seealso:: :wiki:`Working with Files and Media ` + animation (:class:`telegram.Animation`): Optional. Message is an animation, information + about the animation. For backward compatibility, when this field is set, the document + field will also be set. + + .. seealso:: :wiki:`Working with Files and Media ` + game (:class:`telegram.Game`): Optional. Message is a game, information about the game. + :ref:`More about games >> `. + photo (Tuple[:class:`telegram.PhotoSize`]): Optional. Message is a photo, available + sizes of the photo. This list is empty if the message does not contain a photo. + + .. seealso:: :wiki:`Working with Files and Media ` + + .. versionchanged:: 20.0 + |tupleclassattrs| + + sticker (:class:`telegram.Sticker`): Optional. Message is a sticker, information + about the sticker. + + .. seealso:: :wiki:`Working with Files and Media ` + story (:class:`telegram.Story`): Optional. Message is a forwarded story. + + .. versionadded:: 20.5 + video (:class:`telegram.Video`): Optional. Message is a video, information about the + video. + + .. seealso:: :wiki:`Working with Files and Media ` + voice (:class:`telegram.Voice`): Optional. Message is a voice message, information about + the file. + + .. seealso:: :wiki:`Working with Files and Media ` + video_note (:class:`telegram.VideoNote`): Optional. Message is a + `video note `_, information + about the video message. + + .. seealso:: :wiki:`Working with Files and Media ` + new_chat_members (Tuple[:class:`telegram.User`]): Optional. New members that were added + to the group or supergroup and information about them (the bot itself may be one of + these members). This list is empty if the message does not contain new chat members. + + .. versionchanged:: 20.0 + |tupleclassattrs| + caption (:obj:`str`): Optional. Caption for the animation, audio, document, paid media, + photo, video + or voice, 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters. + contact (:class:`telegram.Contact`): Optional. Message is a shared contact, information + about the contact. + location (:class:`telegram.Location`): Optional. Message is a shared location, information + about the location. + venue (:class:`telegram.Venue`): Optional. Message is a venue, information about the + venue. For backward compatibility, when this field is set, the location field will + also be set. + left_chat_member (:class:`telegram.User`): Optional. A member was removed from the group, + information about them (this member may be the bot itself). + new_chat_title (:obj:`str`): Optional. A chat title was changed to this value. + new_chat_photo (Tuple[:class:`telegram.PhotoSize`]): A chat photo was changed to + this value. This list is empty if the message does not contain a new chat photo. + + .. versionchanged:: 20.0 + |tupleclassattrs| + + delete_chat_photo (:obj:`bool`): Optional. Service message: The chat photo was deleted. + group_chat_created (:obj:`bool`): Optional. Service message: The group has been created. + supergroup_chat_created (:obj:`bool`): Optional. Service message: The supergroup has been + created. This field can't be received in a message coming through updates, because bot + can't be a member of a supergroup when it is created. It can only be found in + :attr:`reply_to_message` if someone replies to a very first message in a directly + created supergroup. + channel_chat_created (:obj:`bool`): Optional. Service message: The channel has been + created. This field can't be received in a message coming through updates, because bot + can't be a member of a channel when it is created. It can only be found in + :attr:`reply_to_message` if someone replies to a very first message in a channel. + message_auto_delete_timer_changed (:class:`telegram.MessageAutoDeleteTimerChanged`): + Optional. Service message: auto-delete timer settings changed in the chat. + + .. versionadded:: 13.4 + migrate_to_chat_id (:obj:`int`): Optional. The group has been migrated to a supergroup + with the specified identifier. + migrate_from_chat_id (:obj:`int`): Optional. The supergroup has been migrated from a group + with the specified identifier. + pinned_message (:class:`telegram.MaybeInaccessibleMessage`): Optional. Specified message + was pinned. Note that the Message object in this field will not contain further + :attr:`reply_to_message` fields even if it is itself a reply. + + .. versionchanged:: 20.8 + This attribute now is either :class:`telegram.Message` or + :class:`telegram.InaccessibleMessage`. + invoice (:class:`telegram.Invoice`): Optional. Message is an invoice for a payment, + information about the invoice. + :ref:`More about payments >> `. + successful_payment (:class:`telegram.SuccessfulPayment`): Optional. Message is a service + message about a successful payment, information about the payment. + :ref:`More about payments >> `. + connected_website (:obj:`str`): Optional. The domain name of the website on which the user + has logged in. + `More about Telegram Login >> `_. + author_signature (:obj:`str`): Optional. Signature of the post author for messages in + channels, or the custom title of an anonymous group administrator. + passport_data (:class:`telegram.PassportData`): Optional. Telegram Passport data. + + Examples: + :any:`Passport Bot ` + poll (:class:`telegram.Poll`): Optional. Message is a native poll, + information about the poll. + dice (:class:`telegram.Dice`): Optional. Message is a dice with random value. + via_bot (:class:`telegram.User`): Optional. Bot through which message was sent. + proximity_alert_triggered (:class:`telegram.ProximityAlertTriggered`): Optional. Service + message. A user in the chat triggered another user's proximity alert while sharing + Live Location. + video_chat_scheduled (:class:`telegram.VideoChatScheduled`): Optional. Service message: + video chat scheduled. + + .. versionadded:: 20.0 + video_chat_started (:class:`telegram.VideoChatStarted`): Optional. Service message: video + chat started. + + .. versionadded:: 20.0 + video_chat_ended (:class:`telegram.VideoChatEnded`): Optional. Service message: video chat + ended. + + .. versionadded:: 20.0 + video_chat_participants_invited (:class:`telegram.VideoChatParticipantsInvited`): Optional. + Service message: new participants invited to a video chat. + + .. versionadded:: 20.0 + web_app_data (:class:`telegram.WebAppData`): Optional. Service message: data sent by a Web + App. + + .. versionadded:: 20.0 + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. :paramref:`~telegram.InlineKeyboardButton.login_url` buttons are + represented as ordinary url buttons. + is_topic_message (:obj:`bool`): Optional. :obj:`True`, if the message is sent to a forum + topic. + + .. versionadded:: 20.0 + message_thread_id (:obj:`int`): Optional. Unique identifier of a message thread to which + the message belongs; for supergroups only. + + .. versionadded:: 20.0 + forum_topic_created (:class:`telegram.ForumTopicCreated`): Optional. Service message: + forum topic created. + + .. versionadded:: 20.0 + forum_topic_closed (:class:`telegram.ForumTopicClosed`): Optional. Service message: + forum topic closed. + + .. versionadded:: 20.0 + forum_topic_reopened (:class:`telegram.ForumTopicReopened`): Optional. Service message: + forum topic reopened. + + .. versionadded:: 20.0 + forum_topic_edited (:class:`telegram.ForumTopicEdited`): Optional. Service message: + forum topic edited. + + .. versionadded:: 20.0 + general_forum_topic_hidden (:class:`telegram.GeneralForumTopicHidden`): Optional. + Service message: General forum topic hidden. + + .. versionadded:: 20.0 + general_forum_topic_unhidden (:class:`telegram.GeneralForumTopicUnhidden`): Optional. + Service message: General forum topic unhidden. + + .. versionadded:: 20.0 + write_access_allowed (:class:`telegram.WriteAccessAllowed`): Optional. Service message: + the user allowed the bot added to the attachment menu to write messages. + + .. versionadded:: 20.0 + has_media_spoiler (:obj:`bool`): Optional. :obj:`True`, if the message media is covered + by a spoiler animation. + + .. versionadded:: 20.0 + users_shared (:class:`telegram.UsersShared`): Optional. Service message: users were shared + with the bot + + .. versionadded:: 20.8 + chat_shared (:class:`telegram.ChatShared`): Optional. Service message: a chat was shared + with the bot. + + .. versionadded:: 20.1 + giveaway_created (:class:`telegram.GiveawayCreated`): Optional. Service message: a + scheduled giveaway was created + + .. versionadded:: 20.8 + giveaway (:class:`telegram.Giveaway`): Optional. The message is a scheduled giveaway + message + + .. versionadded:: 20.8 + giveaway_winners (:class:`telegram.GiveawayWinners`): Optional. A giveaway with public + winners was completed + + .. versionadded:: 20.8 + giveaway_completed (:class:`telegram.GiveawayCompleted`): Optional. Service message: a + giveaway without public winners was completed + + .. versionadded:: 20.8 + external_reply (:class:`telegram.ExternalReplyInfo`): Optional. Information about the + message that is being replied to, which may come from another chat or forum topic. + + .. versionadded:: 20.8 + quote (:class:`telegram.TextQuote`): Optional. For replies that quote part of the original + message, the quoted part of the message. + + .. versionadded:: 20.8 + forward_origin (:class:`telegram.MessageOrigin`): Optional. Information about the original + message for forwarded messages + + .. versionadded:: 20.8 + reply_to_story (:class:`telegram.Story`): Optional. For replies to a story, the original + story. + + .. versionadded:: 21.0 + boost_added (:class:`telegram.ChatBoostAdded`): Optional. Service message: user boosted + the chat. + + .. versionadded:: 21.0 + sender_boost_count (:obj:`int`): Optional. If the sender of the + message boosted the chat, the number of boosts added by the user. + + .. versionadded:: 21.0 + + business_connection_id (:obj:`str`): Optional. Unique identifier of the business connection + from which the message was received. If non-empty, the message belongs to a chat of the + corresponding business account that is independent from any potential bot chat which + might share the same identifier. + + .. versionadded:: 21.1 + + sender_business_bot (:class:`telegram.User`): Optional. The bot that actually sent the + message on behalf of the business account. Available only for outgoing messages sent + on behalf of the connected business account. + + .. versionadded:: 21.1 + + chat_background_set (:class:`telegram.ChatBackground`): Optional. Service message: chat + background set + + .. versionadded:: 21.2 + paid_media (:class:`telegram.PaidMediaInfo`): Optional. Message contains paid media; + information about the paid media. + + .. versionadded:: 21.4 + refunded_payment (:class:`telegram.RefundedPayment`): Optional. Message is a service + message about a refunded payment, information about the payment. + + .. versionadded:: 21.4 + + .. |custom_emoji_no_md1_support| replace:: Since custom emoji entities are not supported by + :attr:`~telegram.constants.ParseMode.MARKDOWN`, this method now raises a + :exc:`ValueError` when encountering a custom emoji. + + .. |blockquote_no_md1_support| replace:: Since block quotation entities are not supported + by :attr:`~telegram.constants.ParseMode.MARKDOWN`, this method now raises a + :exc:`ValueError` when encountering a block quotation. + + .. |reply_same_thread| replace:: If :paramref:`message_thread_id` is not provided, + this will reply to the same thread (topic) of the original message. + """ + + # fmt: on + __slots__ = ( + "_effective_attachment", + "animation", + "audio", + "author_signature", + "boost_added", + "business_connection_id", + "caption", + "caption_entities", + "channel_chat_created", + "chat_background_set", + "chat_shared", + "connected_website", + "contact", + "delete_chat_photo", + "dice", + "document", + "edit_date", + "effect_id", + "entities", + "external_reply", + "forum_topic_closed", + "forum_topic_created", + "forum_topic_edited", + "forum_topic_reopened", + "forward_origin", + "from_user", + "game", + "general_forum_topic_hidden", + "general_forum_topic_unhidden", + "giveaway", + "giveaway_completed", + "giveaway_created", + "giveaway_winners", + "group_chat_created", + "has_media_spoiler", + "has_protected_content", + "invoice", + "is_automatic_forward", + "is_from_offline", + "is_topic_message", + "left_chat_member", + "link_preview_options", + "location", + "media_group_id", + "message_auto_delete_timer_changed", + "message_thread_id", + "migrate_from_chat_id", + "migrate_to_chat_id", + "new_chat_members", + "new_chat_photo", + "new_chat_title", + "paid_media", + "passport_data", + "photo", + "pinned_message", + "poll", + "proximity_alert_triggered", + "quote", + "refunded_payment", + "reply_markup", + "reply_to_message", + "reply_to_story", + "sender_boost_count", + "sender_business_bot", + "sender_chat", + "show_caption_above_media", + "sticker", + "story", + "successful_payment", + "supergroup_chat_created", + "text", + "users_shared", + "venue", + "via_bot", + "video", + "video_chat_ended", + "video_chat_participants_invited", + "video_chat_scheduled", + "video_chat_started", + "video_note", + "voice", + "web_app_data", + "write_access_allowed", + ) + + def __init__( + self, + message_id: int, + date: datetime.datetime, + chat: Chat, + from_user: Optional[User] = None, + reply_to_message: Optional["Message"] = None, + edit_date: Optional[datetime.datetime] = None, + text: Optional[str] = None, + entities: Optional[Sequence["MessageEntity"]] = None, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + audio: Optional[Audio] = None, + document: Optional[Document] = None, + game: Optional[Game] = None, + photo: Optional[Sequence[PhotoSize]] = None, + sticker: Optional[Sticker] = None, + video: Optional[Video] = None, + voice: Optional[Voice] = None, + video_note: Optional[VideoNote] = None, + new_chat_members: Optional[Sequence[User]] = None, + caption: Optional[str] = None, + contact: Optional[Contact] = None, + location: Optional[Location] = None, + venue: Optional[Venue] = None, + left_chat_member: Optional[User] = None, + new_chat_title: Optional[str] = None, + new_chat_photo: Optional[Sequence[PhotoSize]] = None, + delete_chat_photo: Optional[bool] = None, + group_chat_created: Optional[bool] = None, + supergroup_chat_created: Optional[bool] = None, + channel_chat_created: Optional[bool] = None, + migrate_to_chat_id: Optional[int] = None, + migrate_from_chat_id: Optional[int] = None, + pinned_message: Optional[MaybeInaccessibleMessage] = None, + invoice: Optional[Invoice] = None, + successful_payment: Optional[SuccessfulPayment] = None, + author_signature: Optional[str] = None, + media_group_id: Optional[str] = None, + connected_website: Optional[str] = None, + animation: Optional[Animation] = None, + passport_data: Optional[PassportData] = None, + poll: Optional[Poll] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + dice: Optional[Dice] = None, + via_bot: Optional[User] = None, + proximity_alert_triggered: Optional[ProximityAlertTriggered] = None, + sender_chat: Optional[Chat] = None, + video_chat_started: Optional[VideoChatStarted] = None, + video_chat_ended: Optional[VideoChatEnded] = None, + video_chat_participants_invited: Optional[VideoChatParticipantsInvited] = None, + message_auto_delete_timer_changed: Optional[MessageAutoDeleteTimerChanged] = None, + video_chat_scheduled: Optional[VideoChatScheduled] = None, + is_automatic_forward: Optional[bool] = None, + has_protected_content: Optional[bool] = None, + web_app_data: Optional[WebAppData] = None, + is_topic_message: Optional[bool] = None, + message_thread_id: Optional[int] = None, + forum_topic_created: Optional[ForumTopicCreated] = None, + forum_topic_closed: Optional[ForumTopicClosed] = None, + forum_topic_reopened: Optional[ForumTopicReopened] = None, + forum_topic_edited: Optional[ForumTopicEdited] = None, + general_forum_topic_hidden: Optional[GeneralForumTopicHidden] = None, + general_forum_topic_unhidden: Optional[GeneralForumTopicUnhidden] = None, + write_access_allowed: Optional[WriteAccessAllowed] = None, + has_media_spoiler: Optional[bool] = None, + chat_shared: Optional[ChatShared] = None, + story: Optional[Story] = None, + giveaway: Optional["Giveaway"] = None, + giveaway_completed: Optional["GiveawayCompleted"] = None, + giveaway_created: Optional["GiveawayCreated"] = None, + giveaway_winners: Optional["GiveawayWinners"] = None, + users_shared: Optional[UsersShared] = None, + link_preview_options: Optional[LinkPreviewOptions] = None, + external_reply: Optional["ExternalReplyInfo"] = None, + quote: Optional["TextQuote"] = None, + forward_origin: Optional["MessageOrigin"] = None, + reply_to_story: Optional[Story] = None, + boost_added: Optional[ChatBoostAdded] = None, + sender_boost_count: Optional[int] = None, + business_connection_id: Optional[str] = None, + sender_business_bot: Optional[User] = None, + is_from_offline: Optional[bool] = None, + chat_background_set: Optional[ChatBackground] = None, + effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + paid_media: Optional[PaidMediaInfo] = None, + refunded_payment: Optional[RefundedPayment] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(chat=chat, message_id=message_id, date=date, api_kwargs=api_kwargs) + + with self._unfrozen(): + # Required + self.message_id: int = message_id + # Optionals + self.from_user: Optional[User] = from_user + self.sender_chat: Optional[Chat] = sender_chat + self.date: datetime.datetime = date + self.chat: Chat = chat + self.is_automatic_forward: Optional[bool] = is_automatic_forward + self.reply_to_message: Optional[Message] = reply_to_message + self.edit_date: Optional[datetime.datetime] = edit_date + self.has_protected_content: Optional[bool] = has_protected_content + self.text: Optional[str] = text + self.entities: Tuple[MessageEntity, ...] = parse_sequence_arg(entities) + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.audio: Optional[Audio] = audio + self.game: Optional[Game] = game + self.document: Optional[Document] = document + self.photo: Tuple[PhotoSize, ...] = parse_sequence_arg(photo) + self.sticker: Optional[Sticker] = sticker + self.video: Optional[Video] = video + self.voice: Optional[Voice] = voice + self.video_note: Optional[VideoNote] = video_note + self.caption: Optional[str] = caption + self.contact: Optional[Contact] = contact + self.location: Optional[Location] = location + self.venue: Optional[Venue] = venue + self.new_chat_members: Tuple[User, ...] = parse_sequence_arg(new_chat_members) + self.left_chat_member: Optional[User] = left_chat_member + self.new_chat_title: Optional[str] = new_chat_title + self.new_chat_photo: Tuple[PhotoSize, ...] = parse_sequence_arg(new_chat_photo) + self.delete_chat_photo: Optional[bool] = bool(delete_chat_photo) + self.group_chat_created: Optional[bool] = bool(group_chat_created) + self.supergroup_chat_created: Optional[bool] = bool(supergroup_chat_created) + self.migrate_to_chat_id: Optional[int] = migrate_to_chat_id + self.migrate_from_chat_id: Optional[int] = migrate_from_chat_id + self.channel_chat_created: Optional[bool] = bool(channel_chat_created) + self.message_auto_delete_timer_changed: Optional[MessageAutoDeleteTimerChanged] = ( + message_auto_delete_timer_changed + ) + self.pinned_message: Optional[MaybeInaccessibleMessage] = pinned_message + self.invoice: Optional[Invoice] = invoice + self.successful_payment: Optional[SuccessfulPayment] = successful_payment + self.connected_website: Optional[str] = connected_website + self.author_signature: Optional[str] = author_signature + self.media_group_id: Optional[str] = media_group_id + self.animation: Optional[Animation] = animation + self.passport_data: Optional[PassportData] = passport_data + self.poll: Optional[Poll] = poll + self.dice: Optional[Dice] = dice + self.via_bot: Optional[User] = via_bot + self.proximity_alert_triggered: Optional[ProximityAlertTriggered] = ( + proximity_alert_triggered + ) + self.video_chat_scheduled: Optional[VideoChatScheduled] = video_chat_scheduled + self.video_chat_started: Optional[VideoChatStarted] = video_chat_started + self.video_chat_ended: Optional[VideoChatEnded] = video_chat_ended + self.video_chat_participants_invited: Optional[VideoChatParticipantsInvited] = ( + video_chat_participants_invited + ) + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.web_app_data: Optional[WebAppData] = web_app_data + self.is_topic_message: Optional[bool] = is_topic_message + self.message_thread_id: Optional[int] = message_thread_id + self.forum_topic_created: Optional[ForumTopicCreated] = forum_topic_created + self.forum_topic_closed: Optional[ForumTopicClosed] = forum_topic_closed + self.forum_topic_reopened: Optional[ForumTopicReopened] = forum_topic_reopened + self.forum_topic_edited: Optional[ForumTopicEdited] = forum_topic_edited + self.general_forum_topic_hidden: Optional[GeneralForumTopicHidden] = ( + general_forum_topic_hidden + ) + self.general_forum_topic_unhidden: Optional[GeneralForumTopicUnhidden] = ( + general_forum_topic_unhidden + ) + self.write_access_allowed: Optional[WriteAccessAllowed] = write_access_allowed + self.has_media_spoiler: Optional[bool] = has_media_spoiler + self.users_shared: Optional[UsersShared] = users_shared + self.chat_shared: Optional[ChatShared] = chat_shared + self.story: Optional[Story] = story + self.giveaway: Optional[Giveaway] = giveaway + self.giveaway_completed: Optional[GiveawayCompleted] = giveaway_completed + self.giveaway_created: Optional[GiveawayCreated] = giveaway_created + self.giveaway_winners: Optional[GiveawayWinners] = giveaway_winners + self.link_preview_options: Optional[LinkPreviewOptions] = link_preview_options + self.external_reply: Optional[ExternalReplyInfo] = external_reply + self.quote: Optional[TextQuote] = quote + self.forward_origin: Optional[MessageOrigin] = forward_origin + self.reply_to_story: Optional[Story] = reply_to_story + self.boost_added: Optional[ChatBoostAdded] = boost_added + self.sender_boost_count: Optional[int] = sender_boost_count + self.business_connection_id: Optional[str] = business_connection_id + self.sender_business_bot: Optional[User] = sender_business_bot + self.is_from_offline: Optional[bool] = is_from_offline + self.chat_background_set: Optional[ChatBackground] = chat_background_set + self.effect_id: Optional[str] = effect_id + self.show_caption_above_media: Optional[bool] = show_caption_above_media + self.paid_media: Optional[PaidMediaInfo] = paid_media + self.refunded_payment: Optional[RefundedPayment] = refunded_payment + + self._effective_attachment = DEFAULT_NONE + + self._id_attrs = (self.message_id, self.chat) + + @property + def chat_id(self) -> int: + """:obj:`int`: Shortcut for :attr:`telegram.Chat.id` for :attr:`chat`.""" + return self.chat.id + + @property + def id(self) -> int: + """ + :obj:`int`: Shortcut for :attr:`message_id`. + + .. versionadded:: 20.0 + """ + return self.message_id + + @property + def link(self) -> Optional[str]: + """:obj:`str`: Convenience property. If the chat of the message is not + a private chat or normal group, returns a t.me link of the message. + + .. versionchanged:: 20.3 + For messages that are replies or part of a forum topic, the link now points + to the corresponding thread view. + """ + if self.chat.type not in [Chat.PRIVATE, Chat.GROUP]: + # the else block gets rid of leading -100 for supergroups: + to_link = self.chat.username if self.chat.username else f"c/{str(self.chat.id)[4:]}" + baselink = f"https://t.me/{to_link}/{self.message_id}" + + # adds the thread for topics and replies + if (self.is_topic_message and self.message_thread_id) or self.reply_to_message: + baselink = f"{baselink}?thread={self.message_thread_id}" + return baselink + return None + + @classmethod + def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["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["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["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["reply_markup"] = InlineKeyboardMarkup.de_json(data.get("reply_markup"), bot) + data["video_chat_scheduled"] = VideoChatScheduled.de_json( + data.get("video_chat_scheduled"), 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["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["forum_topic_reopened"] = ForumTopicReopened.de_json( + data.get("forum_topic_reopened"), 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["general_forum_topic_unhidden"] = GeneralForumTopicUnhidden.de_json( + data.get("general_forum_topic_unhidden"), bot + ) + data["write_access_allowed"] = WriteAccessAllowed.de_json( + data.get("write_access_allowed"), 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 + Giveaway, + GiveawayCompleted, + GiveawayCreated, + GiveawayWinners, + ) + from telegram._messageorigin import ( # pylint: disable=import-outside-toplevel + MessageOrigin, + ) + from telegram._reply import ( # pylint: disable=import-outside-toplevel + ExternalReplyInfo, + 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["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) + + 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 + for key in ( + "user_shared", + "forward_from", + "forward_from_chat", + "forward_from_message_id", + "forward_signature", + "forward_sender_name", + "forward_date", + ): + if entry := data.get(key): + api_kwargs = {key: entry} + + return super()._de_json( # type: ignore[return-value] + data=data, bot=bot, api_kwargs=api_kwargs + ) + + @property + def effective_attachment( + self, + ) -> Union[ + Animation, + Audio, + Contact, + Dice, + Document, + Game, + Invoice, + Location, + PassportData, + Sequence[PhotoSize], + PaidMediaInfo, + Poll, + Sticker, + Story, + SuccessfulPayment, + Venue, + Video, + VideoNote, + Voice, + None, + ]: + """If the message is a user generated content which is not a plain text message, this + property is set to this content. It may be one of + + * :class:`telegram.Audio` + * :class:`telegram.Dice` + * :class:`telegram.Contact` + * :class:`telegram.Document` + * :class:`telegram.Animation` + * :class:`telegram.Game` + * :class:`telegram.Invoice` + * :class:`telegram.Location` + * :class:`telegram.PassportData` + * List[:class:`telegram.PhotoSize`] + * :class:`telegram.PaidMediaInfo` + * :class:`telegram.Poll` + * :class:`telegram.Sticker` + * :class:`telegram.Story` + * :class:`telegram.SuccessfulPayment` + * :class:`telegram.Venue` + * :class:`telegram.Video` + * :class:`telegram.VideoNote` + * :class:`telegram.Voice` + + Otherwise :obj:`None` is returned. + + .. seealso:: :wiki:`Working with Files and Media ` + + .. versionchanged:: 20.0 + :attr:`dice`, :attr:`passport_data` and :attr:`poll` are now also considered to be an + attachment. + + .. versionchanged:: 21.4 + :attr:`paid_media` is now also considered to be an attachment. + + .. deprecated:: 21.4 + :attr:`successful_payment` will be removed in future major versions. + + """ + if not isinstance(self._effective_attachment, DefaultValue): + return self._effective_attachment + + for attachment_type in MessageAttachmentType: + if self[attachment_type]: + self._effective_attachment = self[attachment_type] # type: ignore[assignment] + if attachment_type == MessageAttachmentType.SUCCESSFUL_PAYMENT: + warn( + PTBDeprecationWarning( + "21.4", + "successful_payment will no longer be considered an attachment in" + " future major versions", + ), + stacklevel=2, + ) + break + else: + self._effective_attachment = None + + return self._effective_attachment # type: ignore[return-value] + + def _quote( + self, quote: Optional[bool], reply_to_message_id: Optional[int] = None + ) -> Optional[ReplyParameters]: + """Modify kwargs for replying with or without quoting.""" + if reply_to_message_id is not None: + return ReplyParameters(reply_to_message_id) + + if quote is not None: + if quote: + return ReplyParameters(self.message_id) + + else: + # Unfortunately we need some ExtBot logic here because it's hard to move shortcut + # logic into ExtBot + if hasattr(self.get_bot(), "defaults") and self.get_bot().defaults: # type: ignore + default_quote = self.get_bot().defaults.quote # type: ignore[attr-defined] + else: + default_quote = None + if (default_quote is None and self.chat.type != Chat.PRIVATE) or default_quote: + return ReplyParameters(self.message_id) + + return None + + def compute_quote_position_and_entities( + self, quote: str, index: Optional[int] = None + ) -> Tuple[int, Optional[Tuple[MessageEntity, ...]]]: + """ + Use this function to compute position and entities of a quote in the message text or + caption. Useful for filling the parameters + :paramref:`~telegram.ReplyParameters.quote_position` and + :paramref:`~telegram.ReplyParameters.quote_entities` of :class:`telegram.ReplyParameters` + when replying to a message. + + Example: + + Given a message with the text ``"Hello, world! Hello, world!"``, the following code + will return the position and entities of the second occurrence of ``"Hello, world!"``. + + .. code-block:: python + + message.compute_quote_position_and_entities("Hello, world!", 1) + + .. versionadded:: 20.8 + + Args: + quote (:obj:`str`): Part of the message which is to be quoted. This is + expected to have plain text without formatting entities. + index (:obj:`int`, optional): 0-based index of the occurrence of the quote in the + message. If not specified, the first occurrence is used. + + Returns: + Tuple[:obj:`int`, :obj:`None` | Tuple[:class:`~telegram.MessageEntity`, ...]]: On + success, a tuple containing information about quote position and entities is returned. + + Raises: + RuntimeError: If the message has neither :attr:`text` nor :attr:`caption`. + ValueError: If the requested index of quote doesn't exist in the message. + """ + if not (text := (self.text or self.caption)): + raise RuntimeError("This message has neither text nor caption.") + + # Telegram wants the position in UTF-16 code units, so we have to calculate in that space + utf16_text = text.encode(TextEncoding.UTF_16_LE) + utf16_quote = quote.encode(TextEncoding.UTF_16_LE) + effective_index = index or 0 + + matches = list(re.finditer(re.escape(utf16_quote), utf16_text)) + if (length := len(matches)) < effective_index + 1: + raise ValueError( + f"You requested the {index}-th occurrence of '{quote}', but this text appears " + f"only {length} times." + ) + + position = len(utf16_text[: matches[effective_index].start()]) // 2 + length = len(utf16_quote) // 2 + end_position = position + length + + entities = [] + for entity in self.entities or self.caption_entities: + if position <= entity.offset + entity.length and entity.offset <= end_position: + # shift the offset by the position of the quote + offset = max(0, entity.offset - position) + # trim the entity length to the length of the overlap with the quote + e_length = min(end_position, entity.offset + entity.length) - max( + position, entity.offset + ) + if e_length <= 0: + continue + + # create a new entity with the correct offset and length + # looping over slots rather manually accessing the attributes + # is more future-proof + kwargs = {attr: getattr(entity, attr) for attr in entity.__slots__} + kwargs["offset"] = offset + kwargs["length"] = e_length + entities.append(MessageEntity(**kwargs)) + + return position, tuple(entities) or None + + def build_reply_arguments( + self, + quote: Optional[str] = None, + quote_index: Optional[int] = None, + target_chat_id: Optional[Union[int, str]] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + ) -> _ReplyKwargs: + """ + Builds a dictionary with the keys ``chat_id`` and ``reply_parameters``. This dictionary can + be used to reply to a message with the given quote and target chat. + + Examples: + + Usage with :meth:`telegram.Bot.send_message`: + + .. code-block:: python + + await bot.send_message( + text="This is a reply", + **message.build_reply_arguments(quote="Quoted Text") + ) + + Usage with :meth:`reply_text`, replying in the same chat: + + .. code-block:: python + + await message.reply_text( + "This is a reply", + do_quote=message.build_reply_arguments(quote="Quoted Text") + ) + + Usage with :meth:`reply_text`, replying in a different chat: + + .. code-block:: python + + await message.reply_text( + "This is a reply", + do_quote=message.build_reply_arguments( + quote="Quoted Text", + target_chat_id=-100123456789 + ) + ) + + .. versionadded:: 20.8 + + Args: + quote (:obj:`str`, optional): Passed in :meth:`compute_quote_position_and_entities` + as parameter :paramref:`~compute_quote_position_and_entities.quote` to compute + quote entities. Defaults to :obj:`None`. + quote_index (:obj:`int`, optional): Passed in + :meth:`compute_quote_position_and_entities` as parameter + :paramref:`~compute_quote_position_and_entities.quote_index` to compute quote + position. Defaults to :obj:`None`. + target_chat_id (:obj:`int` | :obj:`str`, optional): |chat_id_channel| + Defaults to :attr:`chat_id`. + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| + Will be applied only if the reply happens in the same chat and forum topic. + message_thread_id (:obj:`int`, optional): |message_thread_id| + + Returns: + :obj:`dict`: + """ + target_chat_is_self = target_chat_id in (None, self.chat_id, f"@{self.chat.username}") + + if target_chat_is_self and message_thread_id in ( + None, + self.message_thread_id, + ): + # defaults handling will take place in `Bot._insert_defaults` + effective_aswr: ODVInput[bool] = allow_sending_without_reply + else: + effective_aswr = None + + quote_position, quote_entities = ( + self.compute_quote_position_and_entities(quote, quote_index) if quote else (None, None) + ) + return { # type: ignore[typeddict-item] + "reply_parameters": ReplyParameters( + chat_id=None if target_chat_is_self else self.chat_id, + message_id=self.message_id, + quote=quote, + quote_position=quote_position, + quote_entities=quote_entities, + allow_sending_without_reply=effective_aswr, + ), + "chat_id": target_chat_id or self.chat_id, + } + + async def _parse_quote_arguments( + self, + do_quote: Optional[Union[bool, _ReplyKwargs]], + quote: Optional[bool], + reply_to_message_id: Optional[int], + reply_parameters: Optional["ReplyParameters"], + ) -> Tuple[Union[str, int], ReplyParameters]: + if quote and do_quote: + raise ValueError("The arguments `quote` and `do_quote` are mutually exclusive") + + if reply_to_message_id is not None and reply_parameters is not None: + raise ValueError( + "`reply_to_message_id` and `reply_parameters` are mutually exclusive." + ) + + if quote is not None: + warn( + PTBDeprecationWarning( + "20.8", + "The `quote` parameter is deprecated in favor of the `do_quote` parameter. " + "Please update your code to use `do_quote` instead.", + ), + stacklevel=2, + ) + + effective_do_quote = quote or do_quote + chat_id: Union[str, int] = self.chat_id + + # reply_parameters and reply_to_message_id overrule the do_quote parameter + if reply_parameters is not None: + effective_reply_parameters = reply_parameters + elif reply_to_message_id is not None: + effective_reply_parameters = ReplyParameters(message_id=reply_to_message_id) + elif isinstance(effective_do_quote, dict): + effective_reply_parameters = effective_do_quote["reply_parameters"] + chat_id = effective_do_quote["chat_id"] + else: + effective_reply_parameters = self._quote(effective_do_quote) + + return chat_id, effective_reply_parameters + + def _parse_message_thread_id( + self, + chat_id: Union[str, int], + message_thread_id: ODVInput[int] = DEFAULT_NONE, + ) -> Optional[int]: + # values set by user have the highest priority + if not isinstance(message_thread_id, DefaultValue): + return message_thread_id + + # self.message_thread_id can be used for send_*.param.message_thread_id only if the + # thread is a forum topic. It does not work if the thread is a chain of replies to a + # message in a normal group. In that case, self.message_thread_id is just the message_id + # of the first message in the chain. + if not self.is_topic_message: + return None + + # Setting message_thread_id=self.message_thread_id only makes sense if we're replying in + # the same chat. + return self.message_thread_id if chat_id in {self.chat_id, self.chat.username} else None + + async def reply_text( + self, + text: str, + parse_mode: ODVInput[str] = DEFAULT_NONE, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + disable_web_page_preview: Optional[bool] = None, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_message( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_message`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_message( + chat_id=chat_id, + text=text, + parse_mode=parse_mode, + disable_web_page_preview=disable_web_page_preview, + link_preview_options=link_preview_options, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + entities=entities, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_markdown( + self, + text: str, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + disable_web_page_preview: Optional[bool] = None, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_message( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + parse_mode=ParseMode.MARKDOWN, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + Sends a message with Markdown version 1 formatting. + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_message`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Note: + :tg-const:`telegram.constants.ParseMode.MARKDOWN` is a legacy mode, retained by + Telegram for backward compatibility. You should use :meth:`reply_markdown_v2` instead. + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_message( + chat_id=chat_id, + text=text, + parse_mode=ParseMode.MARKDOWN, + disable_web_page_preview=disable_web_page_preview, + link_preview_options=link_preview_options, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + entities=entities, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_markdown_v2( + self, + text: str, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + disable_web_page_preview: Optional[bool] = None, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_message( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + parse_mode=ParseMode.MARKDOWN_V2, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + Sends a message with markdown version 2 formatting. + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_message`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_message( + chat_id=chat_id, + text=text, + parse_mode=ParseMode.MARKDOWN_V2, + disable_web_page_preview=disable_web_page_preview, + link_preview_options=link_preview_options, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + entities=entities, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_html( + self, + text: str, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + disable_web_page_preview: Optional[bool] = None, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_message( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + parse_mode=ParseMode.HTML, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + Sends a message with HTML formatting. + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_message`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_message( + chat_id=chat_id, + text=text, + parse_mode=ParseMode.HTML, + disable_web_page_preview=disable_web_page_preview, + link_preview_options=link_preview_options, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + entities=entities, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_media_group( + self, + media: Sequence[ + Union["InputMediaAudio", "InputMediaDocument", "InputMediaPhoto", "InputMediaVideo"] + ], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + ) -> Tuple["Message", ...]: + """Shortcut for:: + + await bot.send_media_group( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_media_group`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + Tuple[:class:`telegram.Message`]: An array of the sent Messages. + + Raises: + :class:`telegram.error.TelegramError` + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_media_group( + chat_id=chat_id, + media=media, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_photo( + self, + photo: Union[FileInput, "PhotoSize"], + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + has_spoiler: Optional[bool] = None, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_photo( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_photo`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_photo( + chat_id=chat_id, + photo=photo, + caption=caption, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + parse_mode=parse_mode, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + has_spoiler=has_spoiler, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, + ) + + async def reply_audio( + self, + audio: Union[FileInput, "Audio"], + duration: Optional[int] = None, + performer: Optional[str] = None, + title: Optional[str] = None, + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_audio( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_audio`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_audio( + chat_id=chat_id, + audio=audio, + duration=duration, + performer=performer, + title=title, + caption=caption, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + parse_mode=parse_mode, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + thumbnail=thumbnail, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_document( + self, + document: Union[FileInput, "Document"], + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + disable_content_type_detection: Optional[bool] = None, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_document( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_document`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_document( + chat_id=chat_id, + document=document, + filename=filename, + caption=caption, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + parse_mode=parse_mode, + api_kwargs=api_kwargs, + disable_content_type_detection=disable_content_type_detection, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + protect_content=protect_content, + message_thread_id=message_thread_id, + thumbnail=thumbnail, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_animation( + self, + animation: Union[FileInput, "Animation"], + duration: Optional[int] = None, + width: Optional[int] = None, + height: Optional[int] = None, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + has_spoiler: Optional[bool] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_animation( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_animation`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_animation( + chat_id=chat_id, + animation=animation, + duration=duration, + width=width, + height=height, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + has_spoiler=has_spoiler, + thumbnail=thumbnail, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, + ) + + async def reply_sticker( + self, + sticker: Union[FileInput, "Sticker"], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + emoji: Optional[str] = None, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_sticker( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_sticker`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_sticker( + chat_id=chat_id, + sticker=sticker, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + emoji=emoji, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_video( + self, + video: Union[FileInput, "Video"], + duration: Optional[int] = None, + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + width: Optional[int] = None, + height: Optional[int] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + supports_streaming: Optional[bool] = None, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + has_spoiler: Optional[bool] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_video( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_video`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_video( + chat_id=chat_id, + video=video, + duration=duration, + caption=caption, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + width=width, + height=height, + parse_mode=parse_mode, + supports_streaming=supports_streaming, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + has_spoiler=has_spoiler, + thumbnail=thumbnail, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, + ) + + async def reply_video_note( + self, + video_note: Union[FileInput, "VideoNote"], + duration: Optional[int] = None, + length: Optional[int] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_video_note( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_video_note`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_video_note( + chat_id=chat_id, + video_note=video_note, + duration=duration, + length=length, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + thumbnail=thumbnail, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_voice( + self, + voice: Union[FileInput, "Voice"], + duration: Optional[int] = None, + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_voice( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_voice`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_voice( + chat_id=chat_id, + voice=voice, + duration=duration, + caption=caption, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + parse_mode=parse_mode, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_location( + self, + latitude: Optional[float] = None, + longitude: Optional[float] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + live_period: Optional[int] = None, + horizontal_accuracy: Optional[float] = None, + heading: Optional[int] = None, + proximity_alert_radius: Optional[int] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + location: Optional[Location] = None, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_location( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_location`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_location( + chat_id=chat_id, + latitude=latitude, + longitude=longitude, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + location=location, + live_period=live_period, + api_kwargs=api_kwargs, + horizontal_accuracy=horizontal_accuracy, + heading=heading, + proximity_alert_radius=proximity_alert_radius, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_venue( + self, + latitude: Optional[float] = None, + longitude: Optional[float] = None, + title: Optional[str] = None, + address: Optional[str] = None, + foursquare_id: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + foursquare_type: Optional[str] = None, + google_place_id: Optional[str] = None, + google_place_type: Optional[str] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + venue: Optional[Venue] = None, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_venue( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_venue`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_venue( + chat_id=chat_id, + latitude=latitude, + longitude=longitude, + title=title, + address=address, + foursquare_id=foursquare_id, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + venue=venue, + foursquare_type=foursquare_type, + api_kwargs=api_kwargs, + google_place_id=google_place_id, + google_place_type=google_place_type, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_contact( + self, + phone_number: Optional[str] = None, + first_name: Optional[str] = None, + last_name: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + vcard: Optional[str] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + contact: Optional[Contact] = None, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_contact( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_contact`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_contact( + chat_id=chat_id, + phone_number=phone_number, + first_name=first_name, + last_name=last_name, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + contact=contact, + vcard=vcard, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_poll( + self, + question: str, + options: Sequence[Union[str, "InputPollOption"]], + is_anonymous: Optional[bool] = None, + type: Optional[str] = None, # pylint: disable=redefined-builtin + allows_multiple_answers: Optional[bool] = None, + correct_option_id: Optional[CorrectOptionID] = None, + is_closed: Optional[bool] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + explanation: Optional[str] = None, + explanation_parse_mode: ODVInput[str] = DEFAULT_NONE, + open_period: Optional[int] = None, + close_date: Optional[Union[int, datetime.datetime]] = None, + explanation_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + question_parse_mode: ODVInput[str] = DEFAULT_NONE, + question_entities: Optional[Sequence["MessageEntity"]] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_poll( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_poll`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_poll( + chat_id=chat_id, + question=question, + options=options, + is_anonymous=is_anonymous, + type=type, + allows_multiple_answers=allows_multiple_answers, + correct_option_id=correct_option_id, + is_closed=is_closed, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + explanation=explanation, + explanation_parse_mode=explanation_parse_mode, + open_period=open_period, + close_date=close_date, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + explanation_entities=explanation_entities, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=self.business_connection_id, + question_parse_mode=question_parse_mode, + question_entities=question_entities, + message_effect_id=message_effect_id, + ) + + async def reply_dice( + self, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + emoji: Optional[str] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_dice( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_dice`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_dice( + chat_id=chat_id, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + emoji=emoji, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_chat_action( + self, + action: str, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.send_chat_action( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_chat_action`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + .. versionadded:: 13.2 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().send_chat_action( + chat_id=self.chat_id, + message_thread_id=self._parse_message_thread_id(self.chat_id, message_thread_id), + action=action, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=self.business_connection_id, + ) + + async def reply_game( + self, + game_short_name: str, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_game( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + business_connection_id=self.business_connection_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_game`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + .. versionadded:: 13.2 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_game( + chat_id=chat_id, # type: ignore[arg-type] + game_short_name=game_short_name, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + ) + + async def reply_invoice( + self, + title: str, + description: str, + payload: str, + provider_token: Optional[str], + currency: str, + prices: Sequence["LabeledPrice"], + start_parameter: Optional[str] = None, + photo_url: Optional[str] = None, + photo_size: Optional[int] = None, + photo_width: Optional[int] = None, + photo_height: Optional[int] = None, + need_name: Optional[bool] = None, + need_phone_number: Optional[bool] = None, + need_email: Optional[bool] = None, + need_shipping_address: Optional[bool] = None, + is_flexible: Optional[bool] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + provider_data: Optional[Union[str, object]] = None, + send_phone_number_to_provider: Optional[bool] = None, + send_email_to_provider: Optional[bool] = None, + max_tip_amount: Optional[int] = None, + suggested_tip_amounts: Optional[Sequence[int]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_invoice( + update.effective_message.chat_id, + message_thread_id=update.effective_message.message_thread_id, + *args, + **kwargs, + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_invoice`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Warning: + As of API 5.2 :paramref:`start_parameter ` + is an optional argument and therefore the + order of the arguments had to be changed. Use keyword arguments to make sure that the + arguments are passed correctly. + + .. versionadded:: 13.2 + + .. versionchanged:: 13.5 + As of Bot API 5.2, the parameter + :paramref:`start_parameter ` is optional. + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().send_invoice( + chat_id=chat_id, + title=title, + description=description, + payload=payload, + provider_token=provider_token, + currency=currency, + prices=prices, + start_parameter=start_parameter, + photo_url=photo_url, + photo_size=photo_size, + photo_width=photo_width, + photo_height=photo_height, + need_name=need_name, + need_phone_number=need_phone_number, + need_email=need_email, + need_shipping_address=need_shipping_address, + is_flexible=is_flexible, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + reply_markup=reply_markup, + provider_data=provider_data, + send_phone_number_to_provider=send_phone_number_to_provider, + send_email_to_provider=send_email_to_provider, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + max_tip_amount=max_tip_amount, + suggested_tip_amounts=suggested_tip_amounts, + protect_content=protect_content, + message_thread_id=message_thread_id, + message_effect_id=message_effect_id, + ) + + async def forward( + self, + chat_id: Union[int, str], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.forward_message( + from_chat_id=update.effective_message.chat_id, + message_id=update.effective_message.message_id, + *args, + **kwargs + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.forward_message`. + + Note: + Since the release of Bot API 5.5 it can be impossible to forward messages from + some chats. Use the attributes :attr:`telegram.Message.has_protected_content` and + :attr:`telegram.ChatFullInfo.has_protected_content` to check this. + + As a workaround, it is still possible to use :meth:`copy`. However, this + behaviour is undocumented and might be changed by Telegram. + + Returns: + :class:`telegram.Message`: On success, instance representing the message forwarded. + + """ + return await self.get_bot().forward_message( + chat_id=chat_id, + from_chat_id=self.chat_id, + message_id=self.message_id, + disable_notification=disable_notification, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def copy( + self, + chat_id: Union[int, str], + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "MessageId": + """Shortcut for:: + + await bot.copy_message( + chat_id=chat_id, + from_chat_id=update.effective_message.chat_id, + message_id=update.effective_message.message_id, + *args, + **kwargs + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.copy_message`. + + Returns: + :class:`telegram.MessageId`: On success, returns the MessageId of the sent message. + + """ + return await self.get_bot().copy_message( + chat_id=chat_id, + from_chat_id=self.chat_id, + message_id=self.message_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + allow_sending_without_reply=allow_sending_without_reply, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + protect_content=protect_content, + message_thread_id=message_thread_id, + show_caption_above_media=show_caption_above_media, + ) + + async def reply_copy( + self, + from_chat_id: Union[str, int], + message_id: int, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: ODVInput[int] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + quote: Optional[bool] = None, + do_quote: Optional[Union[bool, _ReplyKwargs]] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "MessageId": + """Shortcut for:: + + await bot.copy_message( + chat_id=message.chat.id, + message_thread_id=update.effective_message.message_thread_id, + message_id=message_id, + *args, + **kwargs + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.copy_message`. + + .. versionchanged:: 21.1 + |reply_same_thread| + + Keyword Args: + quote (:obj:`bool`, optional): |reply_quote| + + .. versionadded:: 13.1 + .. deprecated:: 20.8 + This argument is deprecated in favor of :paramref:`do_quote` + do_quote (:obj:`bool` | :obj:`dict`, optional): |do_quote| + Mutually exclusive with :paramref:`quote`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.MessageId`: On success, returns the MessageId of the sent message. + + """ + chat_id, effective_reply_parameters = await self._parse_quote_arguments( + do_quote, quote, reply_to_message_id, reply_parameters + ) + message_thread_id = self._parse_message_thread_id(chat_id, message_thread_id) + return await self.get_bot().copy_message( + chat_id=chat_id, + from_chat_id=from_chat_id, + message_id=message_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + disable_notification=disable_notification, + reply_parameters=effective_reply_parameters, + allow_sending_without_reply=allow_sending_without_reply, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + protect_content=protect_content, + message_thread_id=message_thread_id, + show_caption_above_media=show_caption_above_media, + ) + + async def edit_text( + self, + text: str, + parse_mode: ODVInput[str] = DEFAULT_NONE, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + entities: Optional[Sequence["MessageEntity"]] = None, + link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, + *, + disable_web_page_preview: Optional[bool] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union["Message", bool]: + """Shortcut for:: + + await bot.edit_message_text( + chat_id=message.chat_id, + message_id=message.message_id, + business_connection_id=message.business_connection_id, + *args, **kwargs + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.edit_message_text`. + + Note: + You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family + of methods) or channel posts, if the bot is an admin in that channel. However, this + behaviour is undocumented and might be changed by Telegram. + + .. versionchanged:: 21.4 + Now also passes :attr:`business_connection_id`. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by the bot, the + edited Message is returned, otherwise ``True`` is returned. + + """ + return await self.get_bot().edit_message_text( + chat_id=self.chat_id, + message_id=self.message_id, + text=text, + parse_mode=parse_mode, + disable_web_page_preview=disable_web_page_preview, + link_preview_options=link_preview_options, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + entities=entities, + inline_message_id=None, + business_connection_id=self.business_connection_id, + ) + + async def edit_caption( + self, + caption: Optional[str] = None, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + show_caption_above_media: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union["Message", bool]: + """Shortcut for:: + + await bot.edit_message_caption( + chat_id=message.chat_id, + message_id=message.message_id, + business_connection_id=message.business_connection_id, + *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_message_caption`. + + Note: + You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family + of methods) or channel posts, if the bot is an admin in that channel. However, this + behaviour is undocumented and might be changed by Telegram. + + .. versionchanged:: 21.4 + Now also passes :attr:`business_connection_id`. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by the bot, the + edited Message is returned, otherwise ``True`` is returned. + + """ + return await self.get_bot().edit_message_caption( + chat_id=self.chat_id, + message_id=self.message_id, + caption=caption, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + parse_mode=parse_mode, + api_kwargs=api_kwargs, + caption_entities=caption_entities, + inline_message_id=None, + show_caption_above_media=show_caption_above_media, + business_connection_id=self.business_connection_id, + ) + + async def edit_media( + self, + media: "InputMedia", + reply_markup: Optional["InlineKeyboardMarkup"] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union["Message", bool]: + """Shortcut for:: + + await bot.edit_message_media( + chat_id=message.chat_id, + message_id=message.message_id, + business_connection_id=message.business_connection_id, + *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_message_media`. + + Note: + You can only edit messages that the bot sent itself(i.e. of the ``bot.send_*`` family + of methods) or channel posts, if the bot is an admin in that channel. However, this + behaviour is undocumented and might be changed by Telegram. + + .. versionchanged:: 21.4 + Now also passes :attr:`business_connection_id`. + + Returns: + :class:`telegram.Message`: On success, if edited message is not an inline message, the + edited Message is returned, otherwise ``True`` is returned. + + """ + return await self.get_bot().edit_message_media( + media=media, + chat_id=self.chat_id, + message_id=self.message_id, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + inline_message_id=None, + business_connection_id=self.business_connection_id, + ) + + async def edit_reply_markup( + self, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union["Message", bool]: + """Shortcut for:: + + await bot.edit_message_reply_markup( + chat_id=message.chat_id, + message_id=message.message_id, + business_connection_id=message.business_connection_id, + *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_message_reply_markup`. + + Note: + You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family + of methods) or channel posts, if the bot is an admin in that channel. However, this + behaviour is undocumented and might be changed by Telegram. + + .. versionchanged:: 21.4 + Now also passes :attr:`business_connection_id`. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by the bot, the + edited Message is returned, otherwise ``True`` is returned. + """ + return await self.get_bot().edit_message_reply_markup( + chat_id=self.chat_id, + message_id=self.message_id, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + inline_message_id=None, + business_connection_id=self.business_connection_id, + ) + + async def edit_live_location( + self, + latitude: Optional[float] = None, + longitude: Optional[float] = None, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + horizontal_accuracy: Optional[float] = None, + heading: Optional[int] = None, + proximity_alert_radius: Optional[int] = None, + live_period: Optional[int] = None, + *, + location: Optional[Location] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union["Message", bool]: + """Shortcut for:: + + await bot.edit_message_live_location( + chat_id=message.chat_id, + message_id=message.message_id, + business_connection_id=message.business_connection_id, + *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_message_live_location`. + + Note: + You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family + of methods) or channel posts, if the bot is an admin in that channel. However, this + behaviour is undocumented and might be changed by Telegram. + + .. versionchanged:: 21.4 + Now also passes :attr:`business_connection_id`. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by the bot, the + edited Message is returned, otherwise :obj:`True` is returned. + """ + return await self.get_bot().edit_message_live_location( + chat_id=self.chat_id, + message_id=self.message_id, + latitude=latitude, + longitude=longitude, + location=location, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + horizontal_accuracy=horizontal_accuracy, + heading=heading, + proximity_alert_radius=proximity_alert_radius, + live_period=live_period, + inline_message_id=None, + business_connection_id=self.business_connection_id, + ) + + async def stop_live_location( + self, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union["Message", bool]: + """Shortcut for:: + + await bot.stop_message_live_location( + chat_id=message.chat_id, + message_id=message.message_id, + business_connection_id=message.business_connection_id, + *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.stop_message_live_location`. + + Note: + You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family + of methods) or channel posts, if the bot is an admin in that channel. However, this + behaviour is undocumented and might be changed by Telegram. + + .. versionchanged:: 21.4 + Now also passes :attr:`business_connection_id`. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by the bot, the + edited Message is returned, otherwise :obj:`True` is returned. + """ + return await self.get_bot().stop_message_live_location( + chat_id=self.chat_id, + message_id=self.message_id, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + inline_message_id=None, + business_connection_id=self.business_connection_id, + ) + + async def set_game_score( + self, + user_id: int, + score: int, + force: Optional[bool] = None, + disable_edit_message: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Union["Message", bool]: + """Shortcut for:: + + await bot.set_game_score( + chat_id=message.chat_id, message_id=message.message_id, *args, **kwargs + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.set_game_score`. + + Note: + You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family + of methods) or channel posts, if the bot is an admin in that channel. However, this + behaviour is undocumented and might be changed by Telegram. + + Returns: + :class:`telegram.Message`: On success, if edited message is sent by the bot, the + edited Message is returned, otherwise :obj:`True` is returned. + """ + return await self.get_bot().set_game_score( + chat_id=self.chat_id, + message_id=self.message_id, + user_id=user_id, + score=score, + force=force, + disable_edit_message=disable_edit_message, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + inline_message_id=None, + ) + + async def get_game_high_scores( + self, + user_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple["GameHighScore", ...]: + """Shortcut for:: + + await bot.get_game_high_scores( + chat_id=message.chat_id, message_id=message.message_id, *args, **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.get_game_high_scores`. + + Note: + You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family + of methods) or channel posts, if the bot is an admin in that channel. However, this + behaviour is undocumented and might be changed by Telegram. + + Returns: + Tuple[:class:`telegram.GameHighScore`] + """ + return await self.get_bot().get_game_high_scores( + chat_id=self.chat_id, + message_id=self.message_id, + user_id=user_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + inline_message_id=None, + ) + + async def delete( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.delete_message( + chat_id=message.chat_id, message_id=message.message_id, *args, **kwargs + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.delete_message`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().delete_message( + chat_id=self.chat_id, + message_id=self.message_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def stop_poll( + self, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Poll: + """Shortcut for:: + + await bot.stop_poll( + chat_id=message.chat_id, + message_id=message.message_id, + business_connection_id=message.business_connection_id, + *args, **kwargs + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.stop_poll`. + + .. versionchanged:: 21.4 + Now also passes :attr:`business_connection_id`. + + Returns: + :class:`telegram.Poll`: On success, the stopped Poll with the final results is + returned. + + """ + return await self.get_bot().stop_poll( + chat_id=self.chat_id, + message_id=self.message_id, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=self.business_connection_id, + ) + + async def pin( + self, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.pin_chat_message( + chat_id=message.chat_id, + message_id=message.message_id, + business_connection_id=message.business_connection_id, + *args, **kwargs + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.pin_chat_message`. + + .. versionchanged:: 21.5 + Now also passes :attr:`business_connection_id` to + :meth:`telegram.Bot.pin_chat_message`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().pin_chat_message( + chat_id=self.chat_id, + message_id=self.message_id, + business_connection_id=self.business_connection_id, + disable_notification=disable_notification, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unpin( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.unpin_chat_message( + chat_id=message.chat_id, + message_id=message.message_id, + business_connection_id=message.business_connection_id, + *args, **kwargs + ) + + For the documentation of the arguments, please see :meth:`telegram.Bot.unpin_chat_message`. + + .. versionchanged:: 21.5 + Now also passes :attr:`business_connection_id` to + :meth:`telegram.Bot.pin_chat_message`. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().unpin_chat_message( + chat_id=self.chat_id, + message_id=self.message_id, + business_connection_id=self.business_connection_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def edit_forum_topic( + self, + name: Optional[str] = None, + icon_custom_emoji_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.edit_forum_topic( + chat_id=message.chat_id, message_thread_id=message.message_thread_id, *args, + **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().edit_forum_topic( + chat_id=self.chat_id, + message_thread_id=self.message_thread_id, + name=name, + icon_custom_emoji_id=icon_custom_emoji_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def close_forum_topic( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.close_forum_topic( + chat_id=message.chat_id, message_thread_id=message.message_thread_id, *args, + **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.close_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().close_forum_topic( + chat_id=self.chat_id, + message_thread_id=self.message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def reopen_forum_topic( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.reopen_forum_topic( + chat_id=message.chat_id, message_thread_id=message.message_thread_id, *args, + **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.reopen_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().reopen_forum_topic( + chat_id=self.chat_id, + message_thread_id=self.message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_forum_topic( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.delete_forum_topic( + chat_id=message.chat_id, message_thread_id=message.message_thread_id, *args, + **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.delete_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().delete_forum_topic( + chat_id=self.chat_id, + message_thread_id=self.message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unpin_all_forum_topic_messages( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.unpin_all_forum_topic_messages( + chat_id=message.chat_id, message_thread_id=message.message_thread_id, *args, + **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.unpin_all_forum_topic_messages`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().unpin_all_forum_topic_messages( + chat_id=self.chat_id, + message_thread_id=self.message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_reaction( + self, + reaction: Optional[ + Union[Sequence["ReactionType"], "ReactionType", Sequence[str], str] + ] = None, + is_big: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.set_message_reaction(chat_id=message.chat_id, message_id=message.message_id, + *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.set_message_reaction`. + + .. versionadded:: 20.8 + + Returns: + :obj:`bool` On success, :obj:`True` is returned. + """ + return await self.get_bot().set_message_reaction( + chat_id=self.chat_id, + message_id=self.message_id, + reaction=reaction, + is_big=is_big, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + def parse_entity(self, entity: MessageEntity) -> str: + """Returns the text from a given :class:`telegram.MessageEntity`. + + Note: + This method is present because Telegram calculates the offset and length in + UTF-16 codepoint pairs, which some versions of Python don't handle automatically. + (That is, you can't just slice ``Message.text`` with the offset and length.) + + Args: + entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must + be an entity that belongs to this message. + + Returns: + :obj:`str`: The text of the given entity. + + Raises: + RuntimeError: If the message has no text. + + """ + if not self.text: + raise RuntimeError("This Message has no 'text'.") + + return parse_message_entity(self.text, entity) + + def parse_caption_entity(self, entity: MessageEntity) -> str: + """Returns the text from a given :class:`telegram.MessageEntity`. + + Note: + This method is present because Telegram calculates the offset and length in + UTF-16 codepoint pairs, which some versions of Python don't handle automatically. + (That is, you can't just slice ``Message.caption`` with the offset and length.) + + Args: + entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must + be an entity that belongs to this message. + + Returns: + :obj:`str`: The text of the given entity. + + Raises: + RuntimeError: If the message has no caption. + + """ + if not self.caption: + raise RuntimeError("This Message has no 'caption'.") + + return parse_message_entity(self.caption, entity) + + def parse_entities(self, types: Optional[List[str]] = None) -> Dict[MessageEntity, str]: + """ + Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. + It contains entities from this message filtered by their + :attr:`telegram.MessageEntity.type` attribute as the key, and the text that each entity + belongs to as the value of the :obj:`dict`. + + Note: + This method should always be used instead of the :attr:`entities` attribute, since it + calculates the correct substring from the message text based on UTF-16 codepoints. + See :attr:`parse_entity` for more info. + + Args: + types (List[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as + strings. If the ``type`` attribute of an entity is contained in this list, it will + be returned. Defaults to a list of all types. All types can be found as constants + in :class:`telegram.MessageEntity`. + + Returns: + Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to + the text that belongs to them, calculated based on UTF-16 codepoints. + + """ + return parse_message_entities(self.text, self.entities, types=types) + + def parse_caption_entities( + self, types: Optional[List[str]] = None + ) -> Dict[MessageEntity, str]: + """ + Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. + It contains entities from this message's caption filtered by their + :attr:`telegram.MessageEntity.type` attribute as the key, and the text that each entity + belongs to as the value of the :obj:`dict`. + + Note: + This method should always be used instead of the :attr:`caption_entities` attribute, + since it calculates the correct substring from the message text based on UTF-16 + codepoints. See :attr:`parse_entity` for more info. + + Args: + types (List[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as + strings. If the ``type`` attribute of an entity is contained in this list, it will + be returned. Defaults to a list of all types. All types can be found as constants + in :class:`telegram.MessageEntity`. + + Returns: + Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to + the text that belongs to them, calculated based on UTF-16 codepoints. + + """ + return parse_message_entities(self.caption, self.caption_entities, types=types) + + @classmethod + def _parse_html( + cls, + message_text: Optional[str], + entities: Dict[MessageEntity, str], + urled: bool = False, + offset: int = 0, + ) -> Optional[str]: + if message_text is None: + return None + + utf_16_text = message_text.encode(TextEncoding.UTF_16_LE) + html_text = "" + last_offset = 0 + + sorted_entities = sorted(entities.items(), key=lambda item: item[0].offset) + parsed_entities = [] + + for entity, text in sorted_entities: + if entity in parsed_entities: + continue + + nested_entities = { + e: t + for (e, t) in sorted_entities + if e.offset >= entity.offset + and e.offset + e.length <= entity.offset + entity.length + and e != entity + } + parsed_entities.extend(list(nested_entities.keys())) + + if nested_entities: + escaped_text = cls._parse_html( + text, nested_entities, urled=urled, offset=entity.offset + ) + else: + escaped_text = escape(text) + + if entity.type == MessageEntity.TEXT_LINK: + insert = f'{escaped_text}' + elif entity.type == MessageEntity.TEXT_MENTION and entity.user: + insert = f'{escaped_text}' + elif entity.type == MessageEntity.URL and urled: + insert = f'{escaped_text}' + elif entity.type == MessageEntity.BLOCKQUOTE: + insert = f"
{escaped_text}
" + elif entity.type == MessageEntity.EXPANDABLE_BLOCKQUOTE: + insert = f"
{escaped_text}
" + elif entity.type == MessageEntity.BOLD: + insert = f"{escaped_text}" + elif entity.type == MessageEntity.ITALIC: + insert = f"{escaped_text}" + elif entity.type == MessageEntity.CODE: + insert = f"{escaped_text}" + elif entity.type == MessageEntity.PRE: + if entity.language: + insert = f'
{escaped_text}
' + else: + insert = f"
{escaped_text}
" + elif entity.type == MessageEntity.UNDERLINE: + insert = f"{escaped_text}" + elif entity.type == MessageEntity.STRIKETHROUGH: + insert = f"{escaped_text}" + elif entity.type == MessageEntity.SPOILER: + insert = f'{escaped_text}' + elif entity.type == MessageEntity.CUSTOM_EMOJI: + insert = f'{escaped_text}' + else: + insert = escaped_text + + # Make sure to escape the text that is not part of the entity + # if we're in a nested entity, this is still required, since in that case this + # text is part of the parent entity + html_text += ( + escape( + utf_16_text[last_offset * 2 : (entity.offset - offset) * 2].decode( + TextEncoding.UTF_16_LE + ) + ) + + insert + ) + + last_offset = entity.offset - offset + entity.length + + # see comment above + html_text += escape(utf_16_text[last_offset * 2 :].decode(TextEncoding.UTF_16_LE)) + + return html_text + + @property + def text_html(self) -> str: + """Creates an HTML-formatted string from the markup entities found in the message. + + Use this if you want to retrieve the message text with the entities formatted as HTML in + the same way the original message was formatted. + + Warning: + |text_html| + + .. versionchanged:: 13.10 + Spoiler entities are now formatted as HTML. + + .. versionchanged:: 20.3 + Custom emoji entities are now supported. + + .. versionchanged:: 20.8 + Blockquote entities are now supported. + + Returns: + :obj:`str`: Message text with entities formatted as HTML. + + """ + return self._parse_html(self.text, self.parse_entities(), urled=False) + + @property + def text_html_urled(self) -> str: + """Creates an HTML-formatted string from the markup entities found in the message. + + Use this if you want to retrieve the message text with the entities formatted as HTML. + This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. + + Warning: + |text_html| + + .. versionchanged:: 13.10 + Spoiler entities are now formatted as HTML. + + .. versionchanged:: 20.3 + Custom emoji entities are now supported. + + .. versionchanged:: 20.8 + Blockquote entities are now supported. + + Returns: + :obj:`str`: Message text with entities formatted as HTML. + + """ + return self._parse_html(self.text, self.parse_entities(), urled=True) + + @property + def caption_html(self) -> str: + """Creates an HTML-formatted string from the markup entities found in the message's + caption. + + Use this if you want to retrieve the message caption with the caption entities formatted as + HTML in the same way the original message was formatted. + + Warning: + |text_html| + + .. versionchanged:: 13.10 + Spoiler entities are now formatted as HTML. + + .. versionchanged:: 20.3 + Custom emoji entities are now supported. + + .. versionchanged:: 20.8 + Blockquote entities are now supported. + + Returns: + :obj:`str`: Message caption with caption entities formatted as HTML. + """ + return self._parse_html(self.caption, self.parse_caption_entities(), urled=False) + + @property + def caption_html_urled(self) -> str: + """Creates an HTML-formatted string from the markup entities found in the message's + caption. + + Use this if you want to retrieve the message caption with the caption entities formatted as + HTML. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. + + Warning: + |text_html| + + .. versionchanged:: 13.10 + Spoiler entities are now formatted as HTML. + + .. versionchanged:: 20.3 + Custom emoji entities are now supported. + + .. versionchanged:: 20.8 + Blockquote entities are now supported. + + Returns: + :obj:`str`: Message caption with caption entities formatted as HTML. + """ + return self._parse_html(self.caption, self.parse_caption_entities(), urled=True) + + @classmethod + def _parse_markdown( + cls, + message_text: Optional[str], + entities: Dict[MessageEntity, str], + urled: bool = False, + version: MarkdownVersion = 1, + offset: int = 0, + ) -> Optional[str]: + if version == 1: + for entity_type in ( + MessageEntity.EXPANDABLE_BLOCKQUOTE, + MessageEntity.BLOCKQUOTE, + MessageEntity.CUSTOM_EMOJI, + MessageEntity.SPOILER, + MessageEntity.STRIKETHROUGH, + MessageEntity.UNDERLINE, + ): + if any(entity.type == entity_type for entity in entities): + name = entity_type.name.title().replace("_", " ") # type:ignore[attr-defined] + raise ValueError(f"{name} entities are not supported for Markdown version 1") + + if message_text is None: + return None + + utf_16_text = message_text.encode(TextEncoding.UTF_16_LE) + markdown_text = "" + last_offset = 0 + + sorted_entities = sorted(entities.items(), key=lambda item: item[0].offset) + parsed_entities = [] + + for entity, text in sorted_entities: + if entity in parsed_entities: + continue + + nested_entities = { + e: t + for (e, t) in sorted_entities + if e.offset >= entity.offset + and e.offset + e.length <= entity.offset + entity.length + and e != entity + } + parsed_entities.extend(list(nested_entities.keys())) + + if nested_entities: + if version < 2: + raise ValueError("Nested entities are not supported for Markdown version 1") + + escaped_text = cls._parse_markdown( + text, + nested_entities, + urled=urled, + offset=entity.offset, + version=version, + ) + else: + escaped_text = escape_markdown(text, version=version) + + if entity.type == MessageEntity.TEXT_LINK: + if version == 1: + url = entity.url + else: + # Links need special escaping. Also can't have entities nested within + url = escape_markdown( + entity.url, version=version, entity_type=MessageEntity.TEXT_LINK + ) + insert = f"[{escaped_text}]({url})" + elif entity.type == MessageEntity.TEXT_MENTION and entity.user: + insert = f"[{escaped_text}](tg://user?id={entity.user.id})" + elif entity.type == MessageEntity.URL and urled: + link = text if version == 1 else escaped_text + insert = f"[{link}]({text})" + elif entity.type == MessageEntity.BOLD: + insert = f"*{escaped_text}*" + elif entity.type == MessageEntity.ITALIC: + insert = f"_{escaped_text}_" + elif entity.type == MessageEntity.CODE: + # Monospace needs special escaping. Also can't have entities nested within + insert = f"`{escape_markdown(text, version, MessageEntity.CODE)}`" + elif entity.type == MessageEntity.PRE: + # Monospace needs special escaping. Also can't have entities nested within + code = escape_markdown(text, version=version, entity_type=MessageEntity.PRE) + if entity.language: + prefix = f"```{entity.language}\n" + elif code.startswith("\\"): + prefix = "```" + else: + prefix = "```\n" + insert = f"{prefix}{code}```" + elif entity.type == MessageEntity.UNDERLINE: + insert = f"__{escaped_text}__" + elif entity.type == MessageEntity.STRIKETHROUGH: + insert = f"~{escaped_text}~" + elif entity.type == MessageEntity.SPOILER: + insert = f"||{escaped_text}||" + elif entity.type in (MessageEntity.BLOCKQUOTE, MessageEntity.EXPANDABLE_BLOCKQUOTE): + insert = ">" + "\n>".join(escaped_text.splitlines()) + if entity.type == MessageEntity.EXPANDABLE_BLOCKQUOTE: + insert = f"{insert}||" + elif entity.type == MessageEntity.CUSTOM_EMOJI: + # This should never be needed because ids are numeric but the documentation + # specifically mentions it so here we are + custom_emoji_id = escape_markdown( + entity.custom_emoji_id, + version=version, + entity_type=MessageEntity.CUSTOM_EMOJI, + ) + insert = f"![{escaped_text}](tg://emoji?id={custom_emoji_id})" + else: + insert = escaped_text + + # Make sure to escape the text that is not part of the entity + # if we're in a nested entity, this is still required, since in that case this + # text is part of the parent entity + markdown_text += ( + escape_markdown( + utf_16_text[last_offset * 2 : (entity.offset - offset) * 2].decode( + TextEncoding.UTF_16_LE + ), + version=version, + ) + + insert + ) + + last_offset = entity.offset - offset + entity.length + + # see comment above + markdown_text += escape_markdown( + utf_16_text[last_offset * 2 :].decode(TextEncoding.UTF_16_LE), + version=version, + ) + + return markdown_text + + @property + def text_markdown(self) -> str: + """Creates an Markdown-formatted string from the markup entities found in the message + using :class:`telegram.constants.ParseMode.MARKDOWN`. + + Use this if you want to retrieve the message text with the entities formatted as Markdown + in the same way the original message was formatted. + + Warning: + |text_markdown| + + Note: + :tg-const:`telegram.constants.ParseMode.MARKDOWN` is a legacy mode, retained by + Telegram for backward compatibility. You should use :meth:`text_markdown_v2` instead. + + .. versionchanged:: 20.5 + |custom_emoji_no_md1_support| + + .. versionchanged:: 20.8 + |blockquote_no_md1_support| + + Returns: + :obj:`str`: Message text with entities formatted as Markdown. + + Raises: + :exc:`ValueError`: If the message contains underline, strikethrough, spoiler, + blockquote or nested entities. + + """ + return self._parse_markdown(self.text, self.parse_entities(), urled=False) + + @property + def text_markdown_v2(self) -> str: + """Creates an Markdown-formatted string from the markup entities found in the message + using :class:`telegram.constants.ParseMode.MARKDOWN_V2`. + + Use this if you want to retrieve the message text with the entities formatted as Markdown + in the same way the original message was formatted. + + Warning: + |text_markdown| + + .. versionchanged:: 13.10 + Spoiler entities are now formatted as Markdown V2. + + .. versionchanged:: 20.3 + Custom emoji entities are now supported. + + .. versionchanged:: 20.8 + Blockquote entities are now supported. + + Returns: + :obj:`str`: Message text with entities formatted as Markdown. + """ + return self._parse_markdown(self.text, self.parse_entities(), urled=False, version=2) + + @property + def text_markdown_urled(self) -> str: + """Creates an Markdown-formatted string from the markup entities found in the message + using :class:`telegram.constants.ParseMode.MARKDOWN`. + + Use this if you want to retrieve the message text with the entities formatted as Markdown. + This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. + + Warning: + |text_markdown| + + Note: + :tg-const:`telegram.constants.ParseMode.MARKDOWN` is a legacy mode, retained by + Telegram for backward compatibility. You should use :meth:`text_markdown_v2_urled` + instead. + + .. versionchanged:: 20.5 + |custom_emoji_no_md1_support| + + .. versionchanged:: 20.8 + |blockquote_no_md1_support| + + Returns: + :obj:`str`: Message text with entities formatted as Markdown. + + Raises: + :exc:`ValueError`: If the message contains underline, strikethrough, spoiler, + blockquote or nested entities. + + """ + return self._parse_markdown(self.text, self.parse_entities(), urled=True) + + @property + def text_markdown_v2_urled(self) -> str: + """Creates an Markdown-formatted string from the markup entities found in the message + using :class:`telegram.constants.ParseMode.MARKDOWN_V2`. + + Use this if you want to retrieve the message text with the entities formatted as Markdown. + This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. + + Warning: + |text_markdown| + + .. versionchanged:: 13.10 + Spoiler entities are now formatted as Markdown V2. + + .. versionchanged:: 20.3 + Custom emoji entities are now supported. + + .. versionchanged:: 20.8 + Blockquote entities are now supported. + + Returns: + :obj:`str`: Message text with entities formatted as Markdown. + """ + return self._parse_markdown(self.text, self.parse_entities(), urled=True, version=2) + + @property + def caption_markdown(self) -> str: + """Creates an Markdown-formatted string from the markup entities found in the message's + caption using :class:`telegram.constants.ParseMode.MARKDOWN`. + + Use this if you want to retrieve the message caption with the caption entities formatted as + Markdown in the same way the original message was formatted. + + Warning: + |text_markdown| + + Note: + :tg-const:`telegram.constants.ParseMode.MARKDOWN` is a legacy mode, retained by + Telegram for backward compatibility. You should use :meth:`caption_markdown_v2` + .. versionchanged:: 20.5 + |custom_emoji_no_md1_support| + + .. versionchanged:: 20.8 + |blockquote_no_md1_support| + + Returns: + :obj:`str`: Message caption with caption entities formatted as Markdown. + + Raises: + :exc:`ValueError`: If the message contains underline, strikethrough, spoiler, + blockquote or nested entities. + + """ + return self._parse_markdown(self.caption, self.parse_caption_entities(), urled=False) + + @property + def caption_markdown_v2(self) -> str: + """Creates an Markdown-formatted string from the markup entities found in the message's + caption using :class:`telegram.constants.ParseMode.MARKDOWN_V2`. + + Use this if you want to retrieve the message caption with the caption entities formatted as + Markdown in the same way the original message was formatted. + + Warning: + |text_markdown| + + .. versionchanged:: 13.10 + Spoiler entities are now formatted as Markdown V2. + + .. versionchanged:: 20.3 + Custom emoji entities are now supported. + + .. versionchanged:: 20.8 + Blockquote entities are now supported. + + Returns: + :obj:`str`: Message caption with caption entities formatted as Markdown. + """ + return self._parse_markdown( + self.caption, self.parse_caption_entities(), urled=False, version=2 + ) + + @property + def caption_markdown_urled(self) -> str: + """Creates an Markdown-formatted string from the markup entities found in the message's + caption using :class:`telegram.constants.ParseMode.MARKDOWN`. + + Use this if you want to retrieve the message caption with the caption entities formatted as + Markdown. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. + + Warning: + |text_markdown| + + Note: + :tg-const:`telegram.constants.ParseMode.MARKDOWN` is a legacy mode, retained by + Telegram for backward compatibility. You should use + :meth:`caption_markdown_v2_urled` instead. + + .. versionchanged:: 20.5 + |custom_emoji_no_md1_support| + + .. versionchanged:: 20.8 + |blockquote_no_md1_support| + + Returns: + :obj:`str`: Message caption with caption entities formatted as Markdown. + + Raises: + :exc:`ValueError`: If the message contains underline, strikethrough, spoiler, + blockquote or nested entities. + + """ + return self._parse_markdown(self.caption, self.parse_caption_entities(), urled=True) + + @property + def caption_markdown_v2_urled(self) -> str: + """Creates an Markdown-formatted string from the markup entities found in the message's + caption using :class:`telegram.constants.ParseMode.MARKDOWN_V2`. + + Use this if you want to retrieve the message caption with the caption entities formatted as + Markdown. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. + + Warning: + |text_markdown| + + .. versionchanged:: 13.10 + Spoiler entities are now formatted as Markdown V2. + + .. versionchanged:: 20.3 + Custom emoji entities are now supported. + + .. versionchanged:: 20.8 + Blockquote entities are now supported. + + Returns: + :obj:`str`: Message caption with caption entities formatted as Markdown. + """ + return self._parse_markdown( + self.caption, self.parse_caption_entities(), urled=True, version=2 + ) diff --git a/_messageautodeletetimerchanged.py b/_messageautodeletetimerchanged.py new file mode 100644 index 0000000000000000000000000000000000000000..0d9f136d9f05ef3f8420aca1ccf99e312a92f3a0 --- /dev/null +++ b/_messageautodeletetimerchanged.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a change in the Telegram message auto +deletion. +""" + +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class MessageAutoDeleteTimerChanged(TelegramObject): + """This object represents a service message about a change in auto-delete timer settings. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`message_auto_delete_time` is equal. + + .. versionadded:: 13.4 + + Args: + message_auto_delete_time (:obj:`int`): New auto-delete time for messages in the + chat. + + Attributes: + message_auto_delete_time (:obj:`int`): New auto-delete time for messages in the + chat. + + """ + + __slots__ = ("message_auto_delete_time",) + + def __init__( + self, + message_auto_delete_time: int, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.message_auto_delete_time: int = message_auto_delete_time + + self._id_attrs = (self.message_auto_delete_time,) + + self._freeze() diff --git a/_messageentity.py b/_messageentity.py new file mode 100644 index 0000000000000000000000000000000000000000..ae675e8e9fdb80b9ec2965d8d938eed13df70e96 --- /dev/null +++ b/_messageentity.py @@ -0,0 +1,411 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram MessageEntity.""" + +import copy +import itertools +from typing import TYPE_CHECKING, Dict, Final, List, Optional, Sequence, Tuple, Union + +from telegram import constants +from telegram._telegramobject import TelegramObject +from telegram._user import User +from telegram._utils import enum +from telegram._utils.strings import TextEncoding +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + +_SEM = Sequence["MessageEntity"] + + +class MessageEntity(TelegramObject): + """ + This object represents one special entity in a text message. For example, hashtags, + usernames, URLs, etc. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type`, :attr:`offset` and :attr:`length` are equal. + + Args: + type (:obj:`str`): Type of the entity. Can be :attr:`MENTION` (@username), + :attr:`HASHTAG` (#hashtag), :attr:`CASHTAG` ($USD), :attr:`BOT_COMMAND` + (/start@jobs_bot), :attr:`URL` (https://telegram.org), + :attr:`EMAIL` (do-not-reply@telegram.org), :attr:`PHONE_NUMBER` (+1-212-555-0123), + :attr:`BOLD` (**bold text**), :attr:`ITALIC` (*italic text*), :attr:`UNDERLINE` + (underlined text), :attr:`STRIKETHROUGH`, :attr:`SPOILER` (spoiler message), + :attr:`BLOCKQUOTE` (block quotation), :attr:`CODE` (monowidth string), :attr:`PRE` + (monowidth block), :attr:`TEXT_LINK` (for clickable text URLs), :attr:`TEXT_MENTION` + (for users without usernames), :attr:`CUSTOM_EMOJI` (for inline custom emoji stickers). + + .. versionadded:: 20.0 + Added inline custom emoji + + .. versionadded:: 20.8 + Added block quotation + offset (:obj:`int`): Offset in UTF-16 code units to the start of the entity. + length (:obj:`int`): Length of the entity in UTF-16 code units. + url (:obj:`str`, optional): For :attr:`TEXT_LINK` only, url that will be opened after + user taps on the text. + user (:class:`telegram.User`, optional): For :attr:`TEXT_MENTION` only, the mentioned + user. + language (:obj:`str`, optional): For :attr:`PRE` only, the programming language of + the entity text. + custom_emoji_id (:obj:`str`, optional): For :attr:`CUSTOM_EMOJI` only, unique identifier + of the custom emoji. Use :meth:`telegram.Bot.get_custom_emoji_stickers` to get full + information about the sticker. + + .. versionadded:: 20.0 + Attributes: + type (:obj:`str`): Type of the entity. Can be :attr:`MENTION` (@username), + :attr:`HASHTAG` (#hashtag), :attr:`CASHTAG` ($USD), :attr:`BOT_COMMAND` + (/start@jobs_bot), :attr:`URL` (https://telegram.org), + :attr:`EMAIL` (do-not-reply@telegram.org), :attr:`PHONE_NUMBER` (+1-212-555-0123), + :attr:`BOLD` (**bold text**), :attr:`ITALIC` (*italic text*), :attr:`UNDERLINE` + (underlined text), :attr:`STRIKETHROUGH`, :attr:`SPOILER` (spoiler message), + :attr:`BLOCKQUOTE` (block quotation), :attr:`CODE` (monowidth string), :attr:`PRE` + (monowidth block), :attr:`TEXT_LINK` (for clickable text URLs), :attr:`TEXT_MENTION` + (for users without usernames), :attr:`CUSTOM_EMOJI` (for inline custom emoji stickers). + + .. versionadded:: 20.0 + Added inline custom emoji + + .. versionadded:: 20.8 + Added block quotation + offset (:obj:`int`): Offset in UTF-16 code units to the start of the entity. + length (:obj:`int`): Length of the entity in UTF-16 code units. + url (:obj:`str`): Optional. For :attr:`TEXT_LINK` only, url that will be opened after + user taps on the text. + user (:class:`telegram.User`): Optional. For :attr:`TEXT_MENTION` only, the mentioned + user. + language (:obj:`str`): Optional. For :attr:`PRE` only, the programming language of + the entity text. + custom_emoji_id (:obj:`str`): Optional. For :attr:`CUSTOM_EMOJI` only, unique identifier + of the custom emoji. Use :meth:`telegram.Bot.get_custom_emoji_stickers` to get full + information about the sticker. + + .. versionadded:: 20.0 + + """ + + __slots__ = ("custom_emoji_id", "language", "length", "offset", "type", "url", "user") + + def __init__( + self, + type: str, # pylint: disable=redefined-builtin + offset: int, + length: int, + url: Optional[str] = None, + user: Optional[User] = None, + language: Optional[str] = None, + custom_emoji_id: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.type: str = enum.get_member(constants.MessageEntityType, type, type) + self.offset: int = offset + self.length: int = length + # Optionals + self.url: Optional[str] = url + self.user: Optional[User] = user + self.language: Optional[str] = language + self.custom_emoji_id: Optional[str] = custom_emoji_id + + self._id_attrs = (self.type, self.offset, self.length) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) + + @staticmethod + def adjust_message_entities_to_utf_16(text: str, entities: _SEM) -> _SEM: + """Utility functionality for converting the offset and length of entities from + Unicode (:obj:`str`) to UTF-16 (``utf-16-le`` encoded :obj:`bytes`). + + Tip: + Only the offsets and lengths calulated in UTF-16 is acceptable by the Telegram Bot API. + If they are calculated using the Unicode string (:obj:`str` object), errors may occur + when the text contains characters that are not in the Basic Multilingual Plane (BMP). + For more information, see `Unicode `_ and + `Plane (Unicode) `_. + + .. versionadded:: 21.4 + + Examples: + Below is a snippet of code that demonstrates how to use this function to convert + entities from Unicode to UTF-16 space. The ``unicode_entities`` are calculated in + Unicode and the `utf_16_entities` are calculated in UTF-16. + + .. code-block:: python + + text = "𠌕 bold 𝄢 italic underlined: 𝛙𝌢𑁍" + unicode_entities = [ + MessageEntity(offset=2, length=4, type=MessageEntity.BOLD), + MessageEntity(offset=9, length=6, type=MessageEntity.ITALIC), + MessageEntity(offset=28, length=3, type=MessageEntity.UNDERLINE), + ] + utf_16_entities = MessageEntity.adjust_message_entities_to_utf_16( + text, unicode_entities + ) + await bot.send_message( + chat_id=123, + text=text, + entities=utf_16_entities, + ) + # utf_16_entities[0]: offset=3, length=4 + # utf_16_entities[1]: offset=11, length=6 + # utf_16_entities[2]: offset=30, length=6 + + Args: + text (:obj:`str`): The text that the entities belong to + entities (Sequence[:class:`telegram.MessageEntity`]): Sequence of entities + with offset and length calculated in Unicode + + Returns: + Sequence[:class:`telegram.MessageEntity`]: Sequence of entities + with offset and length calculated in UTF-16 encoding + """ + # get sorted positions + positions = sorted(itertools.chain(*((x.offset, x.offset + x.length) for x in entities))) + accumulated_length = 0 + # calculate the length of each slice text[:position] in utf-16 accordingly, + # store the position translations + position_translation: Dict[int, int] = {} + for i, position in enumerate(positions): + last_position = positions[i - 1] if i > 0 else 0 + text_slice = text[last_position:position] + accumulated_length += len(text_slice.encode(TextEncoding.UTF_16_LE)) // 2 + position_translation[position] = accumulated_length + # get the final output entities + out = [] + for entity in entities: + translated_positions = position_translation[entity.offset] + translated_length = ( + position_translation[entity.offset + entity.length] - translated_positions + ) + new_entity = copy.copy(entity) + with new_entity._unfrozen(): + new_entity.offset = translated_positions + new_entity.length = translated_length + out.append(new_entity) + return out + + @staticmethod + def shift_entities(by: Union[str, int], entities: _SEM) -> _SEM: + """Utility functionality for shifting the offset of entities by a given amount. + + Examples: + Shifting by an integer amount: + + .. code-block:: python + + text = "Hello, world!" + entities = [ + MessageEntity(offset=0, length=5, type=MessageEntity.BOLD), + MessageEntity(offset=7, length=5, type=MessageEntity.ITALIC), + ] + shifted_entities = MessageEntity.shift_entities(1, entities) + await bot.send_message( + chat_id=123, + text="!" + text, + entities=shifted_entities, + ) + + Shifting using a string: + + .. code-block:: python + + text = "Hello, world!" + prefix = "𝄢" + entities = [ + MessageEntity(offset=0, length=5, type=MessageEntity.BOLD), + MessageEntity(offset=7, length=5, type=MessageEntity.ITALIC), + ] + shifted_entities = MessageEntity.shift_entities(prefix, entities) + await bot.send_message( + chat_id=123, + text=prefix + text, + entities=shifted_entities, + ) + + Tip: + The :paramref:`entities` are *not* modified in place. The function returns a sequence + of new objects. + + .. versionadded:: 21.5 + + Args: + by (:obj:`str` | :obj:`int`): Either the amount to shift the offset by or + a string whose length will be used as the amount to shift the offset by. In this + case, UTF-16 encoding will be used to calculate the length. + entities (Sequence[:class:`telegram.MessageEntity`]): Sequence of entities + + Returns: + Sequence[:class:`telegram.MessageEntity`]: Sequence of entities with the offset shifted + """ + effective_shift = by if isinstance(by, int) else len(by.encode("utf-16-le")) // 2 + + out = [] + for entity in entities: + new_entity = copy.copy(entity) + with new_entity._unfrozen(): + new_entity.offset += effective_shift + out.append(new_entity) + return out + + @classmethod + def concatenate( + cls, + *args: Union[Tuple[str, _SEM], Tuple[str, _SEM, bool]], + ) -> Tuple[str, _SEM]: + """Utility functionality for concatenating two text along with their formatting entities. + + Tip: + This function is useful for prefixing an already formatted text with a new text and its + formatting entities. In particular, it automatically correctly handles UTF-16 encoding. + + Examples: + This example shows a callback function that can be used to add a prefix and suffix to + the message in a :class:`~telegram.ext.CallbackQueryHandler`: + + .. code-block:: python + + async def prefix_message(update: Update, context: ContextTypes.DEFAULT_TYPE): + prefix = "𠌕 bold 𝄢 italic underlined: 𝛙𝌢𑁍 | " + prefix_entities = [ + MessageEntity(offset=2, length=4, type=MessageEntity.BOLD), + MessageEntity(offset=9, length=6, type=MessageEntity.ITALIC), + MessageEntity(offset=28, length=3, type=MessageEntity.UNDERLINE), + ] + suffix = " | 𠌕 bold 𝄢 italic underlined: 𝛙𝌢𑁍" + suffix_entities = [ + MessageEntity(offset=5, length=4, type=MessageEntity.BOLD), + MessageEntity(offset=12, length=6, type=MessageEntity.ITALIC), + MessageEntity(offset=31, length=3, type=MessageEntity.UNDERLINE), + ] + + message = update.effective_message + first = (prefix, prefix_entities, True) + second = (message.text, message.entities) + third = (suffix, suffix_entities, True) + + new_text, new_entities = MessageEntity.concatenate(first, second, third) + await update.callback_query.edit_message_text( + text=new_text, + entities=new_entities, + ) + + Hint: + The entities are *not* modified in place. The function returns a + new sequence of objects. + + .. versionadded:: 21.5 + + Args: + *args (Tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`]] | \ + Tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`], :obj:`bool`]): + Arbitrary number of tuples containing the text and its entities to concatenate. + If the last element of the tuple is a :obj:`bool`, it is used to determine whether + to adjust the entities to UTF-16 via + :meth:`adjust_message_entities_to_utf_16`. UTF-16 adjustment is disabled by + default. + + Returns: + Tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`]]: The concatenated text + and its entities + """ + output_text = "" + output_entities: List[MessageEntity] = [] + for arg in args: + text, entities = arg[0], arg[1] + + if len(arg) > 2 and arg[2] is True: + entities = cls.adjust_message_entities_to_utf_16(text, entities) + + output_entities.extend(cls.shift_entities(output_text, entities)) + output_text += text + + return output_text, output_entities + + ALL_TYPES: Final[List[str]] = list(constants.MessageEntityType) + """List[:obj:`str`]: A list of all available message entity types.""" + BLOCKQUOTE: Final[str] = constants.MessageEntityType.BLOCKQUOTE + """:const:`telegram.constants.MessageEntityType.BLOCKQUOTE` + + .. versionadded:: 20.8 + """ + BOLD: Final[str] = constants.MessageEntityType.BOLD + """:const:`telegram.constants.MessageEntityType.BOLD`""" + BOT_COMMAND: Final[str] = constants.MessageEntityType.BOT_COMMAND + """:const:`telegram.constants.MessageEntityType.BOT_COMMAND`""" + CASHTAG: Final[str] = constants.MessageEntityType.CASHTAG + """:const:`telegram.constants.MessageEntityType.CASHTAG`""" + CODE: Final[str] = constants.MessageEntityType.CODE + """:const:`telegram.constants.MessageEntityType.CODE`""" + CUSTOM_EMOJI: Final[str] = constants.MessageEntityType.CUSTOM_EMOJI + """:const:`telegram.constants.MessageEntityType.CUSTOM_EMOJI` + + .. versionadded:: 20.0 + """ + EMAIL: Final[str] = constants.MessageEntityType.EMAIL + """:const:`telegram.constants.MessageEntityType.EMAIL`""" + EXPANDABLE_BLOCKQUOTE: Final[str] = constants.MessageEntityType.EXPANDABLE_BLOCKQUOTE + """:const:`telegram.constants.MessageEntityType.EXPANDABLE_BLOCKQUOTE` + + .. versionadded:: 21.3 + """ + HASHTAG: Final[str] = constants.MessageEntityType.HASHTAG + """:const:`telegram.constants.MessageEntityType.HASHTAG`""" + ITALIC: Final[str] = constants.MessageEntityType.ITALIC + """:const:`telegram.constants.MessageEntityType.ITALIC`""" + MENTION: Final[str] = constants.MessageEntityType.MENTION + """:const:`telegram.constants.MessageEntityType.MENTION`""" + PHONE_NUMBER: Final[str] = constants.MessageEntityType.PHONE_NUMBER + """:const:`telegram.constants.MessageEntityType.PHONE_NUMBER`""" + PRE: Final[str] = constants.MessageEntityType.PRE + """:const:`telegram.constants.MessageEntityType.PRE`""" + SPOILER: Final[str] = constants.MessageEntityType.SPOILER + """:const:`telegram.constants.MessageEntityType.SPOILER` + + .. versionadded:: 13.10 + """ + STRIKETHROUGH: Final[str] = constants.MessageEntityType.STRIKETHROUGH + """:const:`telegram.constants.MessageEntityType.STRIKETHROUGH`""" + TEXT_LINK: Final[str] = constants.MessageEntityType.TEXT_LINK + """:const:`telegram.constants.MessageEntityType.TEXT_LINK`""" + TEXT_MENTION: Final[str] = constants.MessageEntityType.TEXT_MENTION + """:const:`telegram.constants.MessageEntityType.TEXT_MENTION`""" + UNDERLINE: Final[str] = constants.MessageEntityType.UNDERLINE + """:const:`telegram.constants.MessageEntityType.UNDERLINE`""" + URL: Final[str] = constants.MessageEntityType.URL + """:const:`telegram.constants.MessageEntityType.URL`""" diff --git a/_messageid.py b/_messageid.py new file mode 100644 index 0000000000000000000000000000000000000000..bbfedf4703740737cd9c7136b3764f0b888eba2d --- /dev/null +++ b/_messageid.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents an instance of a Telegram MessageId.""" + +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class MessageId(TelegramObject): + """This object represents a unique message identifier. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`message_id` is equal. + + Args: + message_id (:obj:`int`): Unique message identifier. + + Attributes: + message_id (:obj:`int`): Unique message identifier. + """ + + __slots__ = ("message_id",) + + def __init__(self, message_id: int, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + self.message_id: int = message_id + + self._id_attrs = (self.message_id,) + + self._freeze() diff --git a/_messageorigin.py b/_messageorigin.py new file mode 100644 index 0000000000000000000000000000000000000000..534583adb8beced78e84cf228f9adf039eacba70 --- /dev/null +++ b/_messageorigin.py @@ -0,0 +1,286 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram MessageOigin.""" +import datetime +from typing import TYPE_CHECKING, Dict, Final, Optional, Type + +from telegram import constants +from telegram._chat import Chat +from telegram._telegramobject import TelegramObject +from telegram._user import User +from telegram._utils import enum +from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class MessageOrigin(TelegramObject): + """ + Base class for telegram MessageOrigin object, it can be one of: + + * :class:`MessageOriginUser` + * :class:`MessageOriginHiddenUser` + * :class:`MessageOriginChat` + * :class:`MessageOriginChannel` + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type` and :attr:`date` are equal. + + .. versionadded:: 20.8 + + Args: + type (:obj:`str`): Type of the message origin, can be on of: + :attr:`~telegram.MessageOrigin.USER`, :attr:`~telegram.MessageOrigin.HIDDEN_USER`, + :attr:`~telegram.MessageOrigin.CHAT`, or :attr:`~telegram.MessageOrigin.CHANNEL`. + date (:obj:`datetime.datetime`): Date the message was sent originally. + |datetime_localization| + + Attributes: + type (:obj:`str`): Type of the message origin, can be on of: + :attr:`~telegram.MessageOrigin.USER`, :attr:`~telegram.MessageOrigin.HIDDEN_USER`, + :attr:`~telegram.MessageOrigin.CHAT`, or :attr:`~telegram.MessageOrigin.CHANNEL`. + date (:obj:`datetime.datetime`): Date the message was sent originally. + |datetime_localization| + """ + + __slots__ = ( + "date", + "type", + ) + + USER: Final[str] = constants.MessageOriginType.USER + """:const:`telegram.constants.MessageOriginType.USER`""" + HIDDEN_USER: Final[str] = constants.MessageOriginType.HIDDEN_USER + """:const:`telegram.constants.MessageOriginType.HIDDEN_USER`""" + CHAT: Final[str] = constants.MessageOriginType.CHAT + """:const:`telegram.constants.MessageOriginType.CHAT`""" + CHANNEL: Final[str] = constants.MessageOriginType.CHANNEL + """:const:`telegram.constants.MessageOriginType.CHANNEL`""" + + def __init__( + self, + type: str, # pylint: disable=W0622 + date: datetime.datetime, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required by all subclasses + self.type: str = enum.get_member(constants.MessageOriginType, type, type) + self.date: datetime.datetime = date + + self._id_attrs = ( + self.type, + self.date, + ) + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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, + cls.CHAT: MessageOriginChat, + cls.CHANNEL: MessageOriginChannel, + } + if cls is MessageOrigin and data.get("type") in _class_mapping: + return _class_mapping[data.pop("type")].de_json(data=data, bot=bot) + + loc_tzinfo = extract_tzinfo_from_defaults(bot) + 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) + + if "sender_chat" in data: + data["sender_chat"] = Chat.de_json(data.get("sender_chat"), bot) + + if "chat" in data: + data["chat"] = Chat.de_json(data.get("chat"), bot) + + return super().de_json(data=data, bot=bot) + + +class MessageOriginUser(MessageOrigin): + """ + The message was originally sent by a known user. + + .. versionadded:: 20.8 + + Args: + date (:obj:`datetime.datetime`): Date the message was sent originally. + |datetime_localization| + sender_user (:class:`telegram.User`): User that sent the message originally. + + Attributes: + type (:obj:`str`): Type of the message origin. Always + :tg-const:`~telegram.MessageOrigin.USER`. + date (:obj:`datetime.datetime`): Date the message was sent originally. + |datetime_localization| + sender_user (:class:`telegram.User`): User that sent the message originally. + """ + + __slots__ = ("sender_user",) + + def __init__( + self, + date: datetime.datetime, + sender_user: User, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.USER, date=date, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.sender_user: User = sender_user + + +class MessageOriginHiddenUser(MessageOrigin): + """ + The message was originally sent by an unknown user. + + .. versionadded:: 20.8 + + Args: + date (:obj:`datetime.datetime`): Date the message was sent originally. + |datetime_localization| + sender_user_name (:obj:`str`): Name of the user that sent the message originally. + + Attributes: + type (:obj:`str`): Type of the message origin. Always + :tg-const:`~telegram.MessageOrigin.HIDDEN_USER`. + date (:obj:`datetime.datetime`): Date the message was sent originally. + |datetime_localization| + sender_user_name (:obj:`str`): Name of the user that sent the message originally. + """ + + __slots__ = ("sender_user_name",) + + def __init__( + self, + date: datetime.datetime, + sender_user_name: str, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.HIDDEN_USER, date=date, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.sender_user_name: str = sender_user_name + + +class MessageOriginChat(MessageOrigin): + """ + The message was originally sent on behalf of a chat to a group chat. + + .. versionadded:: 20.8 + + Args: + date (:obj:`datetime.datetime`): Date the message was sent originally. + |datetime_localization| + sender_chat (:class:`telegram.Chat`): Chat that sent the message originally. + author_signature (:obj:`str`, optional): For messages originally sent by an anonymous chat + administrator, original message author signature + + Attributes: + type (:obj:`str`): Type of the message origin. Always + :tg-const:`~telegram.MessageOrigin.CHAT`. + date (:obj:`datetime.datetime`): Date the message was sent originally. + |datetime_localization| + sender_chat (:class:`telegram.Chat`): Chat that sent the message originally. + author_signature (:obj:`str`): Optional. For messages originally sent by an anonymous chat + administrator, original message author signature + """ + + __slots__ = ( + "author_signature", + "sender_chat", + ) + + def __init__( + self, + date: datetime.datetime, + sender_chat: Chat, + author_signature: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.CHAT, date=date, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.sender_chat: Chat = sender_chat + self.author_signature: Optional[str] = author_signature + + +class MessageOriginChannel(MessageOrigin): + """ + The message was originally sent to a channel chat. + + .. versionadded:: 20.8 + + Args: + date (:obj:`datetime.datetime`): Date the message was sent originally. + |datetime_localization| + chat (:class:`telegram.Chat`): Channel chat to which the message was originally sent. + message_id (:obj:`int`): Unique message identifier inside the chat. + author_signature (:obj:`str`, optional): Signature of the original post author. + + Attributes: + type (:obj:`str`): Type of the message origin. Always + :tg-const:`~telegram.MessageOrigin.CHANNEL`. + date (:obj:`datetime.datetime`): Date the message was sent originally. + |datetime_localization| + chat (:class:`telegram.Chat`): Channel chat to which the message was originally sent. + message_id (:obj:`int`): Unique message identifier inside the chat. + author_signature (:obj:`str`): Optional. Signature of the original post author. + """ + + __slots__ = ( + "author_signature", + "chat", + "message_id", + ) + + def __init__( + self, + date: datetime.datetime, + chat: Chat, + message_id: int, + author_signature: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=self.CHANNEL, date=date, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.chat: Chat = chat + self.message_id: int = message_id + self.author_signature: Optional[str] = author_signature diff --git a/_messagereactionupdated.py b/_messagereactionupdated.py new file mode 100644 index 0000000000000000000000000000000000000000..d4d4033a647bee2b3db93c80ecc398b69595f729 --- /dev/null +++ b/_messagereactionupdated.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram MessageReaction Update.""" +from datetime import datetime +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +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.datetime import extract_tzinfo_from_defaults, from_timestamp +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class MessageReactionCountUpdated(TelegramObject): + """This class represents reaction changes on a message with anonymous reactions. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if the :attr:`chat`, :attr:`message_id`, :attr:`date` and :attr:`reactions` + is equal. + + .. versionadded:: 20.8 + + Args: + chat (:class:`telegram.Chat`): The chat containing the message. + message_id (:obj:`int`): Unique message identifier inside the chat. + date (:class:`datetime.datetime`): Date of the change in Unix time + |datetime_localization| + reactions (Sequence[:class:`telegram.ReactionCount`]): List of reactions that are present + on the message + + Attributes: + chat (:class:`telegram.Chat`): The chat containing the message. + message_id (:obj:`int`): Unique message identifier inside the chat. + date (:class:`datetime.datetime`): Date of the change in Unix time + |datetime_localization| + reactions (Tuple[:class:`telegram.ReactionCount`]): List of reactions that are present on + the message + """ + + __slots__ = ( + "chat", + "date", + "message_id", + "reactions", + ) + + def __init__( + self, + chat: Chat, + message_id: int, + date: datetime, + reactions: Sequence[ReactionCount], + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.chat: Chat = chat + self.message_id: int = message_id + self.date: datetime = date + self.reactions: Tuple[ReactionCount, ...] = parse_sequence_arg(reactions) + + self._id_attrs = (self.chat, self.message_id, self.date, self.reactions) + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) + + +class MessageReactionUpdated(TelegramObject): + """This class represents a change of a reaction on a message performed by a user. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if the :attr:`chat`, :attr:`message_id`, :attr:`date`, :attr:`old_reaction` + and :attr:`new_reaction` is equal. + + .. versionadded:: 20.8 + + Args: + chat (:class:`telegram.Chat`): The chat containing the message. + message_id (:obj:`int`): Unique message identifier inside the chat. + date (:class:`datetime.datetime`): Date of the change in Unix time. + |datetime_localization| + old_reaction (Sequence[:class:`telegram.ReactionType`]): Previous list of reaction types + that were set by the user. + new_reaction (Sequence[:class:`telegram.ReactionType`]): New list of reaction types that + were set by the user. + user (:class:`telegram.User`, optional): The user that changed the reaction, if the user + isn't anonymous. + actor_chat (:class:`telegram.Chat`, optional): The chat on behalf of which the reaction was + changed, if the user is anonymous. + + Attributes: + chat (:class:`telegram.Chat`): The chat containing the message. + message_id (:obj:`int`): Unique message identifier inside the chat. + date (:class:`datetime.datetime`): Date of the change in Unix time. + |datetime_localization| + old_reaction (Tuple[:class:`telegram.ReactionType`]): Previous list of reaction types + that were set by the user. + new_reaction (Tuple[:class:`telegram.ReactionType`]): New list of reaction types that + were set by the user. + user (:class:`telegram.User`): Optional. The user that changed the reaction, if the user + isn't anonymous. + actor_chat (:class:`telegram.Chat`): Optional. The chat on behalf of which the reaction was + changed, if the user is anonymous. + """ + + __slots__ = ( + "actor_chat", + "chat", + "date", + "message_id", + "new_reaction", + "old_reaction", + "user", + ) + + def __init__( + self, + chat: Chat, + message_id: int, + date: datetime, + old_reaction: Sequence[ReactionType], + new_reaction: Sequence[ReactionType], + user: Optional[User] = None, + actor_chat: Optional[Chat] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.chat: Chat = chat + self.message_id: int = message_id + self.date: datetime = date + self.old_reaction: Tuple[ReactionType, ...] = parse_sequence_arg(old_reaction) + self.new_reaction: Tuple[ReactionType, ...] = parse_sequence_arg(new_reaction) + + # Optional + self.user: Optional[User] = user + self.actor_chat: Optional[Chat] = actor_chat + + self._id_attrs = ( + self.chat, + self.message_id, + self.date, + self.old_reaction, + self.new_reaction, + ) + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) diff --git a/_paidmedia.py b/_paidmedia.py new file mode 100644 index 0000000000000000000000000000000000000000..fe78cca28e07af350930decfbe64aafa6be57572 --- /dev/null +++ b/_paidmedia.py @@ -0,0 +1,290 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains objects that represent paid media in Telegram.""" + +from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type + +from telegram import constants +from telegram._files.photosize import PhotoSize +from telegram._files.video import Video +from telegram._telegramobject import TelegramObject +from telegram._utils import enum +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class PaidMedia(TelegramObject): + """Describes the paid media added to a message. Currently, it can be one of: + + * :class:`telegram.PaidMediaPreview` + * :class:`telegram.PaidMediaPhoto` + * :class:`telegram.PaidMediaVideo` + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`type` is equal. + + .. versionadded:: 21.4 + + Args: + type (:obj:`str`): Type of the paid media. + + Attributes: + type (:obj:`str`): Type of the paid media. + """ + + __slots__ = ("type",) + + PREVIEW: Final[str] = constants.PaidMediaType.PREVIEW + """:const:`telegram.constants.PaidMediaType.PREVIEW`""" + PHOTO: Final[str] = constants.PaidMediaType.PHOTO + """:const:`telegram.constants.PaidMediaType.PHOTO`""" + VIDEO: Final[str] = constants.PaidMediaType.VIDEO + """:const:`telegram.constants.PaidMediaType.VIDEO`""" + + def __init__( + self, + type: str, # pylint: disable=redefined-builtin + *, + api_kwargs: Optional[JSONDict] = None, + ) -> None: + super().__init__(api_kwargs=api_kwargs) + self.type: str = enum.get_member(constants.PaidMediaType, type, type) + + self._id_attrs = (self.type,) + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["PaidMedia"]: + """Converts JSON data to the appropriate :class:`PaidMedia` object, i.e. takes + care of selecting the correct subclass. + + Args: + data (Dict[:obj:`str`, ...]): The JSON data. + bot (:class:`telegram.Bot`, optional): The bot associated with this object. + + Returns: + The Telegram object. + + """ + 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, + cls.VIDEO: PaidMediaVideo, + } + + if cls is PaidMedia and data.get("type") in _class_mapping: + return _class_mapping[data.pop("type")].de_json(data=data, bot=bot) + + return super().de_json(data=data, bot=bot) + + +class PaidMediaPreview(PaidMedia): + """The paid media isn't available before the payment. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`width`, :attr:`height`, and :attr:`duration` + are equal. + + .. versionadded:: 21.4 + + Args: + type (:obj:`str`): Type of the paid media, always :tg-const:`telegram.PaidMedia.PREVIEW`. + width (:obj:`int`, optional): Media width as defined by the sender. + height (:obj:`int`, optional): Media height as defined by the sender. + duration (:obj:`int`, optional): Duration of the media in seconds as defined by the sender. + + Attributes: + type (:obj:`str`): Type of the paid media, always :tg-const:`telegram.PaidMedia.PREVIEW`. + width (:obj:`int`): Optional. Media width as defined by the sender. + height (:obj:`int`): Optional. Media height as defined by the sender. + duration (:obj:`int`): Optional. Duration of the media in seconds as defined by the sender. + """ + + __slots__ = ("duration", "height", "width") + + def __init__( + self, + width: Optional[int] = None, + height: Optional[int] = None, + duration: Optional[int] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ) -> None: + super().__init__(type=PaidMedia.PREVIEW, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.width: Optional[int] = width + self.height: Optional[int] = height + self.duration: Optional[int] = duration + + self._id_attrs = (self.type, self.width, self.height, self.duration) + + +class PaidMediaPhoto(PaidMedia): + """ + The paid media is a photo. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`photo` are equal. + + .. versionadded:: 21.4 + + Args: + type (:obj:`str`): Type of the paid media, always :tg-const:`telegram.PaidMedia.PHOTO`. + photo (Sequence[:class:`telegram.PhotoSize`]): The photo. + + Attributes: + type (:obj:`str`): Type of the paid media, always :tg-const:`telegram.PaidMedia.PHOTO`. + photo (Tuple[:class:`telegram.PhotoSize`]): The photo. + """ + + __slots__ = ("photo",) + + def __init__( + self, + photo: Sequence["PhotoSize"], + *, + api_kwargs: Optional[JSONDict] = None, + ) -> None: + super().__init__(type=PaidMedia.PHOTO, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.photo: Tuple[PhotoSize, ...] = parse_sequence_arg(photo) + + self._id_attrs = (self.type, self.photo) + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["PaidMediaPhoto"]: + data = cls._parse_data(data) + + if not data: + return None + + data["photo"] = PhotoSize.de_list(data.get("photo"), bot=bot) + return super().de_json(data=data, bot=bot) # type: ignore[return-value] + + +class PaidMediaVideo(PaidMedia): + """ + The paid media is a video. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`video` are equal. + + .. versionadded:: 21.4 + + Args: + type (:obj:`str`): Type of the paid media, always :tg-const:`telegram.PaidMedia.VIDEO`. + video (:class:`telegram.Video`): The video. + + Attributes: + type (:obj:`str`): Type of the paid media, always :tg-const:`telegram.PaidMedia.VIDEO`. + video (:class:`telegram.Video`): The video. + """ + + __slots__ = ("video",) + + def __init__( + self, + video: Video, + *, + api_kwargs: Optional[JSONDict] = None, + ) -> None: + super().__init__(type=PaidMedia.VIDEO, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.video: Video = video + + self._id_attrs = (self.type, self.video) + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["PaidMediaVideo"]: + data = cls._parse_data(data) + + if not data: + return None + + data["video"] = Video.de_json(data.get("video"), bot=bot) + return super().de_json(data=data, bot=bot) # type: ignore[return-value] + + +class PaidMediaInfo(TelegramObject): + """ + Describes the paid media added to a message. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`star_count` and :attr:`paid_media` are equal. + + .. versionadded:: 21.4 + + Args: + star_count (:obj:`int`): The number of Telegram Stars that must be paid to buy access to + the media. + paid_media (Sequence[:class:`telegram.PaidMedia`]): Information about the paid media. + + Attributes: + star_count (:obj:`int`): The number of Telegram Stars that must be paid to buy access to + the media. + paid_media (Tuple[:class:`telegram.PaidMedia`]): Information about the paid media. + """ + + __slots__ = ("paid_media", "star_count") + + def __init__( + self, + star_count: int, + paid_media: Sequence[PaidMedia], + *, + api_kwargs: Optional[JSONDict] = None, + ) -> None: + super().__init__(api_kwargs=api_kwargs) + self.star_count: int = star_count + self.paid_media: Tuple[PaidMedia, ...] = parse_sequence_arg(paid_media) + + self._id_attrs = (self.star_count, self.paid_media) + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["PaidMediaInfo"]: + data = cls._parse_data(data) + + if not data: + return None + + data["paid_media"] = PaidMedia.de_list(data.get("paid_media"), bot=bot) + return super().de_json(data=data, bot=bot) diff --git a/_poll.py b/_poll.py new file mode 100644 index 0000000000000000000000000000000000000000..8ea387a0950298bb1f8990afd92f9d949a086086 --- /dev/null +++ b/_poll.py @@ -0,0 +1,649 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram Poll.""" +import datetime +from typing import TYPE_CHECKING, Dict, Final, List, Optional, Sequence, Tuple + +from telegram import constants +from telegram._chat import Chat +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.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 +from telegram._utils.types import JSONDict, ODVInput + +if TYPE_CHECKING: + from telegram import Bot + + +class InputPollOption(TelegramObject): + """ + This object contains information about one answer option in a poll to be sent. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`text` is equal. + + .. versionadded:: 21.2 + + Args: + text (:obj:`str`): Option text, + :tg-const:`telegram.PollOption.MIN_LENGTH`-:tg-const:`telegram.PollOption.MAX_LENGTH` + characters. + text_parse_mode (:obj:`str`, optional): |parse_mode| + Currently, only custom emoji entities are allowed. + text_entities (Sequence[:class:`telegram.MessageEntity`], optional): Special entities + that appear in the option :paramref:`text`. It can be specified instead of + :paramref:`text_parse_mode`. + Currently, only custom emoji entities are allowed. + This list is empty if the text does not contain entities. + + Attributes: + text (:obj:`str`): Option text, + :tg-const:`telegram.PollOption.MIN_LENGTH`-:tg-const:`telegram.PollOption.MAX_LENGTH` + characters. + text_parse_mode (:obj:`str`): Optional. |parse_mode| + Currently, only custom emoji entities are allowed. + text_entities (Sequence[:class:`telegram.MessageEntity`]): Special entities + that appear in the option :paramref:`text`. It can be specified instead of + :paramref:`text_parse_mode`. + Currently, only custom emoji entities are allowed. + This list is empty if the text does not contain entities. + """ + + __slots__ = ("text", "text_entities", "text_parse_mode") + + def __init__( + self, + text: str, + text_parse_mode: ODVInput[str] = DEFAULT_NONE, + text_entities: Optional[Sequence[MessageEntity]] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.text: str = text + self.text_parse_mode: ODVInput[str] = text_parse_mode + self.text_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(text_entities) + + self._id_attrs = (self.text,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) + + +class PollOption(TelegramObject): + """ + This object contains information about one answer option in a poll. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`text` and :attr:`voter_count` are equal. + + Args: + text (:obj:`str`): Option text, + :tg-const:`telegram.PollOption.MIN_LENGTH`-:tg-const:`telegram.PollOption.MAX_LENGTH` + characters. + voter_count (:obj:`int`): Number of users that voted for this option. + text_entities (Sequence[:class:`telegram.MessageEntity`], optional): Special entities + that appear in the option text. Currently, only custom emoji entities are allowed in + poll option texts. + + .. versionadded:: 21.2 + + Attributes: + text (:obj:`str`): Option text, + :tg-const:`telegram.PollOption.MIN_LENGTH`-:tg-const:`telegram.PollOption.MAX_LENGTH` + characters. + voter_count (:obj:`int`): Number of users that voted for this option. + text_entities (Tuple[:class:`telegram.MessageEntity`]): Special entities + that appear in the option text. Currently, only custom emoji entities are allowed in + poll option texts. + This list is empty if the question does not contain entities. + + .. versionadded:: 21.2 + + """ + + __slots__ = ("text", "text_entities", "voter_count") + + def __init__( + self, + text: str, + voter_count: int, + text_entities: Optional[Sequence[MessageEntity]] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.text: str = text + self.voter_count: int = voter_count + self.text_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(text_entities) + + self._id_attrs = (self.text, self.voter_count) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) + + def parse_entity(self, entity: MessageEntity) -> str: + """Returns the text in :attr:`text` + from a given :class:`telegram.MessageEntity` of :attr:`text_entities`. + + Note: + This method is present because Telegram calculates the offset and length in + UTF-16 codepoint pairs, which some versions of Python don't handle automatically. + (That is, you can't just slice ``Message.text`` with the offset and length.) + + .. versionadded:: 21.2 + + Args: + entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must + be an entity that belongs to :attr:`text_entities`. + + Returns: + :obj:`str`: The text of the given entity. + """ + return parse_message_entity(self.text, entity) + + def parse_entities(self, types: Optional[List[str]] = None) -> Dict[MessageEntity, str]: + """ + Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. + It contains entities from this polls question filtered by their ``type`` attribute as + the key, and the text that each entity belongs to as the value of the :obj:`dict`. + + Note: + This method should always be used instead of the :attr:`text_entities` + attribute, since it calculates the correct substring from the message text based on + UTF-16 codepoints. See :attr:`parse_entity` for more info. + + .. versionadded:: 21.2 + + Args: + types (List[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the + ``type`` attribute of an entity is contained in this list, it will be returned. + Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`. + + Returns: + Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to + the text that belongs to them, calculated based on UTF-16 codepoints. + """ + return parse_message_entities(self.text, self.text_entities, types) + + MIN_LENGTH: Final[int] = constants.PollLimit.MIN_OPTION_LENGTH + """:const:`telegram.constants.PollLimit.MIN_OPTION_LENGTH` + + .. versionadded:: 20.0 + """ + MAX_LENGTH: Final[int] = constants.PollLimit.MAX_OPTION_LENGTH + """:const:`telegram.constants.PollLimit.MAX_OPTION_LENGTH` + + .. versionadded:: 20.0 + """ + + +class PollAnswer(TelegramObject): + """ + This object represents an answer of a user in a non-anonymous poll. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`poll_id`, :attr:`user` and :attr:`option_ids` are equal. + + .. versionchanged:: 20.5 + The order of :paramref:`option_ids` and :paramref:`user` is changed in + 20.5 as the latter one became optional. + + .. versionchanged:: 20.6 + Backward compatiblity for changed order of :paramref:`option_ids` and :paramref:`user` + was removed. + + Args: + poll_id (:obj:`str`): Unique poll identifier. + option_ids (Sequence[:obj:`int`]): Identifiers of answer options, chosen by the user. May + be empty if the user retracted their vote. + + .. versionchanged:: 20.0 + |sequenceclassargs| + user (:class:`telegram.User`, optional): The user that changed the answer to the poll, + if the voter isn't anonymous. If the voter is anonymous, this field will contain the + user :tg-const:`telegram.constants.ChatID.FAKE_CHANNEL` for backwards compatibility. + + .. versionchanged:: 20.5 + :paramref:`user` became optional. + voter_chat (:class:`telegram.Chat`, optional): The chat that changed the answer to the + poll, if the voter is anonymous. + + .. versionadded:: 20.5 + + Attributes: + poll_id (:obj:`str`): Unique poll identifier. + option_ids (Tuple[:obj:`int`]): Identifiers of answer options, chosen by the user. May + be empty if the user retracted their vote. + + .. versionchanged:: 20.0 + |tupleclassattrs| + user (:class:`telegram.User`): Optional. The user, who changed the answer to the + poll, if the voter isn't anonymous. If the voter is anonymous, this field will contain + the user :tg-const:`telegram.constants.ChatID.FAKE_CHANNEL` for backwards compatibility + + .. versionchanged:: 20.5 + :paramref:`user` became optional. + voter_chat (:class:`telegram.Chat`): Optional. The chat that changed the answer to the + poll, if the voter is anonymous. + + .. versionadded:: 20.5 + + """ + + __slots__ = ("option_ids", "poll_id", "user", "voter_chat") + + def __init__( + self, + poll_id: str, + option_ids: Sequence[int], + user: Optional[User] = None, + voter_chat: Optional[Chat] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.poll_id: str = poll_id + self.voter_chat: Optional[Chat] = voter_chat + self.option_ids: Tuple[int, ...] = parse_sequence_arg(option_ids) + self.user: Optional[User] = user + + self._id_attrs = ( + self.poll_id, + self.option_ids, + self.user, + self.voter_chat, + ) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) + + +class Poll(TelegramObject): + """ + This object contains information about a poll. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`id` is equal. + + Examples: + :any:`Poll Bot ` + + Args: + id (:obj:`str`): Unique poll identifier. + question (:obj:`str`): Poll question, :tg-const:`telegram.Poll.MIN_QUESTION_LENGTH`- + :tg-const:`telegram.Poll.MAX_QUESTION_LENGTH` characters. + options (Sequence[:class:`~telegram.PollOption`]): List of poll options. + + .. versionchanged:: 20.0 + |sequenceclassargs| + is_closed (:obj:`bool`): :obj:`True`, if the poll is closed. + is_anonymous (:obj:`bool`): :obj:`True`, if the poll is anonymous. + type (:obj:`str`): Poll type, currently can be :attr:`REGULAR` or :attr:`QUIZ`. + allows_multiple_answers (:obj:`bool`): :obj:`True`, if the poll allows multiple answers. + correct_option_id (:obj:`int`, optional): A zero based identifier of the correct answer + option. Available only for closed polls in the quiz mode, which were sent + (not forwarded), by the bot or to a private chat with the bot. + explanation (:obj:`str`, optional): Text that is shown when a user chooses an incorrect + answer or taps on the lamp icon in a quiz-style poll, + 0-:tg-const:`telegram.Poll.MAX_EXPLANATION_LENGTH` characters. + explanation_entities (Sequence[:class:`telegram.MessageEntity`], optional): Special + entities like usernames, URLs, bot commands, etc. that appear in the + :attr:`explanation`. This list is empty if the message does not contain explanation + entities. + + .. versionchanged:: 20.0 + + * This attribute is now always a (possibly empty) list and never :obj:`None`. + * |sequenceclassargs| + open_period (:obj:`int`, optional): Amount of time in seconds the poll will be active + after creation. + close_date (:obj:`datetime.datetime`, optional): Point in time (Unix timestamp) when the + poll will be automatically closed. Converted to :obj:`datetime.datetime`. + + .. versionchanged:: 20.3 + |datetime_localization| + question_entities (Sequence[:class:`telegram.MessageEntity`], optional): Special entities + that appear in the :attr:`question`. Currently, only custom emoji entities are allowed + in poll questions. + + .. versionadded:: 21.2 + + Attributes: + id (:obj:`str`): Unique poll identifier. + question (:obj:`str`): Poll question, :tg-const:`telegram.Poll.MIN_QUESTION_LENGTH`- + :tg-const:`telegram.Poll.MAX_QUESTION_LENGTH` characters. + options (Tuple[:class:`~telegram.PollOption`]): List of poll options. + + .. versionchanged:: 20.0 + |tupleclassattrs| + total_voter_count (:obj:`int`): Total number of users that voted in the poll. + is_closed (:obj:`bool`): :obj:`True`, if the poll is closed. + is_anonymous (:obj:`bool`): :obj:`True`, if the poll is anonymous. + type (:obj:`str`): Poll type, currently can be :attr:`REGULAR` or :attr:`QUIZ`. + allows_multiple_answers (:obj:`bool`): :obj:`True`, if the poll allows multiple answers. + correct_option_id (:obj:`int`): Optional. A zero based identifier of the correct answer + option. Available only for closed polls in the quiz mode, which were sent + (not forwarded), by the bot or to a private chat with the bot. + explanation (:obj:`str`): Optional. Text that is shown when a user chooses an incorrect + answer or taps on the lamp icon in a quiz-style poll, + 0-:tg-const:`telegram.Poll.MAX_EXPLANATION_LENGTH` characters. + explanation_entities (Tuple[:class:`telegram.MessageEntity`]): Special entities + like usernames, URLs, bot commands, etc. that appear in the :attr:`explanation`. + This list is empty if the message does not contain explanation entities. + + .. versionchanged:: 20.0 + |tupleclassattrs| + + .. versionchanged:: 20.0 + This attribute is now always a (possibly empty) list and never :obj:`None`. + open_period (:obj:`int`): Optional. Amount of time in seconds the poll will be active + after creation. + close_date (:obj:`datetime.datetime`): Optional. Point in time when the poll will be + automatically closed. + + .. versionchanged:: 20.3 + |datetime_localization| + question_entities (Tuple[:class:`telegram.MessageEntity`]): Special entities + that appear in the :attr:`question`. Currently, only custom emoji entities are allowed + in poll questions. + This list is empty if the question does not contain entities. + + .. versionadded:: 21.2 + + """ + + __slots__ = ( + "allows_multiple_answers", + "close_date", + "correct_option_id", + "explanation", + "explanation_entities", + "id", + "is_anonymous", + "is_closed", + "open_period", + "options", + "question", + "question_entities", + "total_voter_count", + "type", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + question: str, + options: Sequence[PollOption], + total_voter_count: int, + is_closed: bool, + is_anonymous: bool, + type: str, # pylint: disable=redefined-builtin + allows_multiple_answers: bool, + correct_option_id: Optional[int] = None, + explanation: Optional[str] = None, + explanation_entities: Optional[Sequence[MessageEntity]] = None, + open_period: Optional[int] = None, + close_date: Optional[datetime.datetime] = None, + question_entities: Optional[Sequence[MessageEntity]] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.id: str = id + self.question: str = question + self.options: Tuple[PollOption, ...] = parse_sequence_arg(options) + self.total_voter_count: int = total_voter_count + self.is_closed: bool = is_closed + self.is_anonymous: bool = is_anonymous + self.type: str = enum.get_member(constants.PollType, type, type) + self.allows_multiple_answers: bool = allows_multiple_answers + self.correct_option_id: Optional[int] = correct_option_id + self.explanation: Optional[str] = explanation + self.explanation_entities: Tuple[MessageEntity, ...] = parse_sequence_arg( + explanation_entities + ) + self.open_period: Optional[int] = open_period + self.close_date: Optional[datetime.datetime] = close_date + self.question_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(question_entities) + + self._id_attrs = (self.id,) + + self._freeze() + + @classmethod + def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["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["close_date"] = from_timestamp(data.get("close_date"), tzinfo=loc_tzinfo) + data["question_entities"] = MessageEntity.de_list(data.get("question_entities"), bot) + + return super().de_json(data=data, bot=bot) + + def parse_explanation_entity(self, entity: MessageEntity) -> str: + """Returns the text in :attr:`explanation` from a given :class:`telegram.MessageEntity` of + :attr:`explanation_entities`. + + Note: + This method is present because Telegram calculates the offset and length in + UTF-16 codepoint pairs, which some versions of Python don't handle automatically. + (That is, you can't just slice ``Message.text`` with the offset and length.) + + Args: + entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must + be an entity that belongs to :attr:`explanation_entities`. + + Returns: + :obj:`str`: The text of the given entity. + + Raises: + RuntimeError: If the poll has no explanation. + + """ + if not self.explanation: + raise RuntimeError("This Poll has no 'explanation'.") + + return parse_message_entity(self.explanation, entity) + + def parse_explanation_entities( + self, types: Optional[List[str]] = None + ) -> Dict[MessageEntity, str]: + """ + Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. + It contains entities from this polls explanation filtered by their ``type`` attribute as + the key, and the text that each entity belongs to as the value of the :obj:`dict`. + + Note: + This method should always be used instead of the :attr:`explanation_entities` + attribute, since it calculates the correct substring from the message text based on + UTF-16 codepoints. See :attr:`parse_explanation_entity` for more info. + + Args: + types (List[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the + ``type`` attribute of an entity is contained in this list, it will be returned. + Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`. + + Returns: + Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to + the text that belongs to them, calculated based on UTF-16 codepoints. + + Raises: + RuntimeError: If the poll has no explanation. + + """ + if not self.explanation: + raise RuntimeError("This Poll has no 'explanation'.") + + return parse_message_entities(self.explanation, self.explanation_entities, types) + + def parse_question_entity(self, entity: MessageEntity) -> str: + """Returns the text in :attr:`question` from a given :class:`telegram.MessageEntity` of + :attr:`question_entities`. + + .. versionadded:: 21.2 + + Note: + This method is present because Telegram calculates the offset and length in + UTF-16 codepoint pairs, which some versions of Python don't handle automatically. + (That is, you can't just slice ``Message.text`` with the offset and length.) + + Args: + entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must + be an entity that belongs to :attr:`question_entities`. + + Returns: + :obj:`str`: The text of the given entity. + """ + return parse_message_entity(self.question, entity) + + def parse_question_entities( + self, types: Optional[List[str]] = None + ) -> Dict[MessageEntity, str]: + """ + Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. + It contains entities from this polls question filtered by their ``type`` attribute as + the key, and the text that each entity belongs to as the value of the :obj:`dict`. + + .. versionadded:: 21.2 + + Note: + This method should always be used instead of the :attr:`question_entities` + attribute, since it calculates the correct substring from the message text based on + UTF-16 codepoints. See :attr:`parse_question_entity` for more info. + + Args: + types (List[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the + ``type`` attribute of an entity is contained in this list, it will be returned. + Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`. + + Returns: + Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to + the text that belongs to them, calculated based on UTF-16 codepoints. + + """ + return parse_message_entities(self.question, self.question_entities, types) + + REGULAR: Final[str] = constants.PollType.REGULAR + """:const:`telegram.constants.PollType.REGULAR`""" + QUIZ: Final[str] = constants.PollType.QUIZ + """:const:`telegram.constants.PollType.QUIZ`""" + MAX_EXPLANATION_LENGTH: Final[int] = constants.PollLimit.MAX_EXPLANATION_LENGTH + """:const:`telegram.constants.PollLimit.MAX_EXPLANATION_LENGTH` + + .. versionadded:: 20.0 + """ + MAX_EXPLANATION_LINE_FEEDS: Final[int] = constants.PollLimit.MAX_EXPLANATION_LINE_FEEDS + """:const:`telegram.constants.PollLimit.MAX_EXPLANATION_LINE_FEEDS` + + .. versionadded:: 20.0 + """ + MIN_OPEN_PERIOD: Final[int] = constants.PollLimit.MIN_OPEN_PERIOD + """:const:`telegram.constants.PollLimit.MIN_OPEN_PERIOD` + + .. versionadded:: 20.0 + """ + MAX_OPEN_PERIOD: Final[int] = constants.PollLimit.MAX_OPEN_PERIOD + """:const:`telegram.constants.PollLimit.MAX_OPEN_PERIOD` + + .. versionadded:: 20.0 + """ + MIN_QUESTION_LENGTH: Final[int] = constants.PollLimit.MIN_QUESTION_LENGTH + """:const:`telegram.constants.PollLimit.MIN_QUESTION_LENGTH` + + .. versionadded:: 20.0 + """ + MAX_QUESTION_LENGTH: Final[int] = constants.PollLimit.MAX_QUESTION_LENGTH + """:const:`telegram.constants.PollLimit.MAX_QUESTION_LENGTH` + + .. versionadded:: 20.0 + """ + MIN_OPTION_LENGTH: Final[int] = constants.PollLimit.MIN_OPTION_LENGTH + """:const:`telegram.constants.PollLimit.MIN_OPTION_LENGTH` + + .. versionadded:: 20.0 + """ + MAX_OPTION_LENGTH: Final[int] = constants.PollLimit.MAX_OPTION_LENGTH + """:const:`telegram.constants.PollLimit.MAX_OPTION_LENGTH` + + .. versionadded:: 20.0 + """ + MIN_OPTION_NUMBER: Final[int] = constants.PollLimit.MIN_OPTION_NUMBER + """:const:`telegram.constants.PollLimit.MIN_OPTION_NUMBER` + + .. versionadded:: 20.0 + """ + MAX_OPTION_NUMBER: Final[int] = constants.PollLimit.MAX_OPTION_NUMBER + """:const:`telegram.constants.PollLimit.MAX_OPTION_NUMBER` + + .. versionadded:: 20.0 + """ diff --git a/_proximityalerttriggered.py b/_proximityalerttriggered.py new file mode 100644 index 0000000000000000000000000000000000000000..0880ca9a6f6fc3b79b9dce9db3a7942f9499da81 --- /dev/null +++ b/_proximityalerttriggered.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram Proximity Alert.""" +from typing import TYPE_CHECKING, Optional + +from telegram._telegramobject import TelegramObject +from telegram._user import User +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class ProximityAlertTriggered(TelegramObject): + """ + This object represents the content of a service message, sent whenever a user in the chat + triggers a proximity alert set by another user. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`traveler`, :attr:`watcher` and :attr:`distance` are equal. + + Args: + traveler (:class:`telegram.User`): User that triggered the alert + watcher (:class:`telegram.User`): User that set the alert + distance (:obj:`int`): The distance between the users + + Attributes: + traveler (:class:`telegram.User`): User that triggered the alert + watcher (:class:`telegram.User`): User that set the alert + distance (:obj:`int`): The distance between the users + + """ + + __slots__ = ("distance", "traveler", "watcher") + + def __init__( + self, + traveler: User, + watcher: User, + distance: int, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.traveler: User = traveler + self.watcher: User = watcher + self.distance: int = distance + + self._id_attrs = (self.traveler, self.watcher, self.distance) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) diff --git a/_reaction.py b/_reaction.py new file mode 100644 index 0000000000000000000000000000000000000000..90de7823d791acb8b7d78c26b28f2a62298286d6 --- /dev/null +++ b/_reaction.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains objects that represents a Telegram ReactionType.""" + +from typing import TYPE_CHECKING, Dict, Final, Literal, Optional, Type, Union + +from telegram import constants +from telegram._telegramobject import TelegramObject +from telegram._utils import enum +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class ReactionType(TelegramObject): + """Base class for Telegram ReactionType Objects. + There exist :class:`telegram.ReactionTypeEmoji`, :class:`telegram.ReactionTypeCustomEmoji` + and :class:`telegram.ReactionTypePaid`. + + .. versionadded:: 20.8 + .. versionchanged:: 21.5 + + Added paid reaction. + + Args: + type (:obj:`str`): Type of the reaction. Can be + :attr:`~telegram.ReactionType.EMOJI`, :attr:`~telegram.ReactionType.CUSTOM_EMOJI` or + :attr:`~telegram.ReactionType.PAID`. + Attributes: + type (:obj:`str`): Type of the reaction. Can be + :attr:`~telegram.ReactionType.EMOJI`, :attr:`~telegram.ReactionType.CUSTOM_EMOJI` or + :attr:`~telegram.ReactionType.PAID`. + + """ + + __slots__ = ("type",) + + EMOJI: Final[constants.ReactionType] = constants.ReactionType.EMOJI + """:const:`telegram.constants.ReactionType.EMOJI`""" + CUSTOM_EMOJI: Final[constants.ReactionType] = constants.ReactionType.CUSTOM_EMOJI + """:const:`telegram.constants.ReactionType.CUSTOM_EMOJI`""" + PAID: Final[constants.ReactionType] = constants.ReactionType.PAID + """:const:`telegram.constants.ReactionType.PAID` + + .. versionadded:: 21.5 + """ + + def __init__( + self, + type: Union[ # pylint: disable=redefined-builtin + Literal["emoji", "custom_emoji", "paid"], constants.ReactionType + ], + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required by all subclasses + self.type: str = enum.get_member(constants.ReactionType, type, type) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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, + cls.PAID: ReactionTypePaid, + } + + if cls is ReactionType and data.get("type") in _class_mapping: + return _class_mapping[data.pop("type")].de_json(data, bot) + + return super().de_json(data=data, bot=bot) + + +class ReactionTypeEmoji(ReactionType): + """ + Represents a reaction with a normal emoji. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if the :attr:`emoji` is equal. + + .. versionadded:: 20.8 + + Args: + emoji (:obj:`str`): Reaction emoji. It can be one of + :const:`telegram.constants.ReactionEmoji`. + + Attributes: + type (:obj:`str`): Type of the reaction, + always :tg-const:`telegram.ReactionType.EMOJI`. + emoji (:obj:`str`): Reaction emoji. It can be one of + :const:`telegram.constants.ReactionEmoji`. + """ + + __slots__ = ("emoji",) + + def __init__( + self, + emoji: str, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=ReactionType.EMOJI, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.emoji: str = emoji + self._id_attrs = (self.emoji,) + + +class ReactionTypeCustomEmoji(ReactionType): + """ + Represents a reaction with a custom emoji. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if the :attr:`custom_emoji_id` is equal. + + .. versionadded:: 20.8 + + Args: + custom_emoji_id (:obj:`str`): Custom emoji identifier. + + Attributes: + type (:obj:`str`): Type of the reaction, + always :tg-const:`telegram.ReactionType.CUSTOM_EMOJI`. + custom_emoji_id (:obj:`str`): Custom emoji identifier. + + """ + + __slots__ = ("custom_emoji_id",) + + def __init__( + self, + custom_emoji_id: str, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(type=ReactionType.CUSTOM_EMOJI, api_kwargs=api_kwargs) + + with self._unfrozen(): + self.custom_emoji_id: str = custom_emoji_id + self._id_attrs = (self.custom_emoji_id,) + + +class ReactionTypePaid(ReactionType): + """ + The reaction is paid. + + .. versionadded:: 21.5 + + Attributes: + type (:obj:`str`): Type of the reaction, + always :tg-const:`telegram.ReactionType.PAID`. + """ + + __slots__ = () + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(type=ReactionType.PAID, api_kwargs=api_kwargs) + self._freeze() + + +class ReactionCount(TelegramObject): + """This class represents a reaction added to a message along with the number of times it was + added. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if the :attr:`type` and :attr:`total_count` is equal. + + .. versionadded:: 20.8 + + Args: + type (:class:`telegram.ReactionType`): Type of the reaction. + total_count (:obj:`int`): Number of times the reaction was added. + + Attributes: + type (:class:`telegram.ReactionType`): Type of the reaction. + total_count (:obj:`int`): Number of times the reaction was added. + """ + + __slots__ = ( + "total_count", + "type", + ) + + def __init__( + self, + type: ReactionType, # pylint: disable=redefined-builtin + total_count: int, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.type: ReactionType = type + self.total_count: int = total_count + + self._id_attrs = ( + self.type, + self.total_count, + ) + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) diff --git a/_reply.py b/_reply.py new file mode 100644 index 0000000000000000000000000000000000000000..65e42665718d390ac848ebc113f808d554997da6 --- /dev/null +++ b/_reply.py @@ -0,0 +1,471 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This modules contains objects that represents Telegram Replies""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple, Union + +from telegram._chat import Chat +from telegram._dice import Dice +from telegram._files.animation import Animation +from telegram._files.audio import Audio +from telegram._files.contact import Contact +from telegram._files.document import Document +from telegram._files.location import Location +from telegram._files.photosize import PhotoSize +from telegram._files.sticker import Sticker +from telegram._files.venue import Venue +from telegram._files.video import Video +from telegram._files.videonote import VideoNote +from telegram._files.voice import Voice +from telegram._games.game import Game +from telegram._giveaway import Giveaway, GiveawayWinners +from telegram._linkpreviewoptions import LinkPreviewOptions +from telegram._messageentity import MessageEntity +from telegram._messageorigin import MessageOrigin +from telegram._paidmedia import PaidMediaInfo +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.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput + +if TYPE_CHECKING: + from telegram import Bot + + +class ExternalReplyInfo(TelegramObject): + """ + This object contains information about a message that is being replied to, which may + come from another chat or forum topic. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`origin` is equal. + + .. versionadded:: 20.8 + + Args: + origin (:class:`telegram.MessageOrigin`): Origin of the message replied to by the given + message. + chat (:class:`telegram.Chat`, optional): Chat the original message belongs to. Available + only if the chat is a supergroup or a channel. + message_id (:obj:`int`, optional): Unique message identifier inside the original chat. + Available only if the original chat is a supergroup or a channel. + link_preview_options (:class:`telegram.LinkPreviewOptions`, optional): Options used for + link preview generation for the original message, if it is a text message + animation (:class:`telegram.Animation`, optional): Message is an animation, information + about the animation. + audio (:class:`telegram.Audio`, optional): Message is an audio file, information about the + file. + document (:class:`telegram.Document`, optional): Message is a general file, information + about the file. + photo (Sequence[:class:`telegram.PhotoSize`], optional): Message is a photo, available + sizes of the photo. + sticker (:class:`telegram.Sticker`, optional): Message is a sticker, information about the + sticker. + story (:class:`telegram.Story`, optional): Message is a forwarded story. + video (:class:`telegram.Video`, optional): Message is a video, information about the video. + video_note (:class:`telegram.VideoNote`, optional): Message is a `video note + `_, information about the video + message. + voice (:class:`telegram.Voice`, optional): Message is a voice message, information about + the file. + has_media_spoiler (:obj:`bool`, optional): :obj:`True`, if the message media is covered by + a spoiler animation. + contact (:class:`telegram.Contact`, optional): Message is a shared contact, information + about the contact. + dice (:class:`telegram.Dice`, optional): Message is a dice with random value. + game (:Class:`telegram.Game`. optional): Message is a game, information about the game. + :ref:`More about games >> `. + giveaway (:class:`telegram.Giveaway`, optional): Message is a scheduled giveaway, + information about the giveaway. + giveaway_winners (:class:`telegram.GiveawayWinners`, optional): A giveaway with public + winners was completed. + invoice (:class:`telegram.Invoice`, optional): Message is an invoice for a payment, + information about the invoice. :ref:`More about payments >> `. + location (:class:`telegram.Location`, optional): Message is a shared location, information + about the location. + poll (:class:`telegram.Poll`, optional): Message is a native poll, information about the + poll. + venue (:class:`telegram.Venue`, optional): Message is a venue, information about the venue. + paid_media (:class:`telegram.PaidMedia`, optional): Message contains paid media; + information about the paid media. + + .. versionadded:: 21.4 + + Attributes: + origin (:class:`telegram.MessageOrigin`): Origin of the message replied to by the given + message. + chat (:class:`telegram.Chat`): Optional. Chat the original message belongs to. Available + only if the chat is a supergroup or a channel. + message_id (:obj:`int`): Optional. Unique message identifier inside the original chat. + Available only if the original chat is a supergroup or a channel. + link_preview_options (:class:`telegram.LinkPreviewOptions`): Optional. Options used for + link preview generation for the original message, if it is a text message. + animation (:class:`telegram.Animation`): Optional. Message is an animation, information + about the animation. + audio (:class:`telegram.Audio`): Optional. Message is an audio file, information about the + file. + document (:class:`telegram.Document`): Optional. Message is a general file, information + about the file. + photo (Tuple[:class:`telegram.PhotoSize`]): Optional. Message is a photo, available sizes + of the photo. + sticker (:class:`telegram.Sticker`): Optional. Message is a sticker, information about the + sticker. + story (:class:`telegram.Story`): Optional. Message is a forwarded story. + video (:class:`telegram.Video`): Optional. Message is a video, information about the video. + video_note (:class:`telegram.VideoNote`): Optional. Message is a `video note + `_, information about the video + message. + voice (:class:`telegram.Voice`): Optional. Message is a voice message, information about + the file. + has_media_spoiler (:obj:`bool`): Optional. :obj:`True`, if the message media is covered by + a spoiler animation. + contact (:class:`telegram.Contact`): Optional. Message is a shared contact, information + about the contact. + dice (:class:`telegram.Dice`): Optional. Message is a dice with random value. + game (:Class:`telegram.Game`): Optional. Message is a game, information about the game. + :ref:`More about games >> `. + giveaway (:class:`telegram.Giveaway`): Optional. Message is a scheduled giveaway, + information about the giveaway. + giveaway_winners (:class:`telegram.GiveawayWinners`): Optional. A giveaway with public + winners was completed. + invoice (:class:`telegram.Invoice`): Optional. Message is an invoice for a payment, + information about the invoice. :ref:`More about payments >> `. + location (:class:`telegram.Location`): Optional. Message is a shared location, information + about the location. + poll (:class:`telegram.Poll`): Optional. Message is a native poll, information about the + poll. + venue (:class:`telegram.Venue`): Optional. Message is a venue, information about the venue. + paid_media (:class:`telegram.PaidMedia`): Optional. Message contains paid media; + information about the paid media. + + .. versionadded:: 21.4 + """ + + __slots__ = ( + "animation", + "audio", + "chat", + "contact", + "dice", + "document", + "game", + "giveaway", + "giveaway_winners", + "has_media_spoiler", + "invoice", + "link_preview_options", + "location", + "message_id", + "origin", + "paid_media", + "photo", + "poll", + "sticker", + "story", + "venue", + "video", + "video_note", + "voice", + ) + + def __init__( + self, + origin: MessageOrigin, + chat: Optional[Chat] = None, + message_id: Optional[int] = None, + link_preview_options: Optional[LinkPreviewOptions] = None, + animation: Optional[Animation] = None, + audio: Optional[Audio] = None, + document: Optional[Document] = None, + photo: Optional[Sequence[PhotoSize]] = None, + sticker: Optional[Sticker] = None, + story: Optional[Story] = None, + video: Optional[Video] = None, + video_note: Optional[VideoNote] = None, + voice: Optional[Voice] = None, + has_media_spoiler: Optional[bool] = None, + contact: Optional[Contact] = None, + dice: Optional[Dice] = None, + game: Optional[Game] = None, + giveaway: Optional[Giveaway] = None, + giveaway_winners: Optional[GiveawayWinners] = None, + invoice: Optional[Invoice] = None, + location: Optional[Location] = None, + poll: Optional[Poll] = None, + venue: Optional[Venue] = None, + paid_media: Optional[PaidMediaInfo] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + self.origin: MessageOrigin = origin + self.chat: Optional[Chat] = chat + self.message_id: Optional[int] = message_id + self.link_preview_options: Optional[LinkPreviewOptions] = link_preview_options + self.animation: Optional[Animation] = animation + self.audio: Optional[Audio] = audio + self.document: Optional[Document] = document + self.photo: Optional[Tuple[PhotoSize, ...]] = parse_sequence_arg(photo) + self.sticker: Optional[Sticker] = sticker + self.story: Optional[Story] = story + self.video: Optional[Video] = video + self.video_note: Optional[VideoNote] = video_note + self.voice: Optional[Voice] = voice + self.has_media_spoiler: Optional[bool] = has_media_spoiler + self.contact: Optional[Contact] = contact + self.dice: Optional[Dice] = dice + self.game: Optional[Game] = game + self.giveaway: Optional[Giveaway] = giveaway + self.giveaway_winners: Optional[GiveawayWinners] = giveaway_winners + self.invoice: Optional[Invoice] = invoice + self.location: Optional[Location] = location + self.poll: Optional[Poll] = poll + self.venue: Optional[Venue] = venue + self.paid_media: Optional[PaidMediaInfo] = paid_media + + self._id_attrs = (self.origin,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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["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) + + return super().de_json(data=data, bot=bot) + + +class TextQuote(TelegramObject): + """ + This object contains information about the quoted part of a message that is replied to + by the given message. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`text` and :attr:`position` are equal. + + .. versionadded:: 20.8 + + Args: + text (:obj:`str`): Text of the quoted part of a message that is replied to by the given + message. + position (:obj:`int`): Approximate quote position in the original message in UTF-16 code + units as specified by the sender. + entities (Sequence[:class:`telegram.MessageEntity`], optional): Special entities that + appear + in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and + custom_emoji entities are kept in quotes. + is_manual (:obj:`bool`, optional): :obj:`True`, if the quote was chosen manually by the + message sender. Otherwise, the quote was added automatically by the server. + + Attributes: + text (:obj:`str`): Text of the quoted part of a message that is replied to by the given + message. + position (:obj:`int`): Approximate quote position in the original message in UTF-16 code + units as specified by the sender. + entities (Tuple[:class:`telegram.MessageEntity`]): Optional. Special entities that appear + in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and + custom_emoji entities are kept in quotes. + is_manual (:obj:`bool`): Optional. :obj:`True`, if the quote was chosen manually by the + message sender. Otherwise, the quote was added automatically by the server. + """ + + __slots__ = ( + "entities", + "is_manual", + "position", + "text", + ) + + def __init__( + self, + text: str, + position: int, + entities: Optional[Sequence[MessageEntity]] = None, + is_manual: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + self.text: str = text + self.position: int = position + self.entities: Optional[Tuple[MessageEntity, ...]] = parse_sequence_arg(entities) + self.is_manual: Optional[bool] = is_manual + + self._id_attrs = ( + self.text, + self.position, + ) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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)) + + return super().de_json(data=data, bot=bot) + + +class ReplyParameters(TelegramObject): + """ + Describes reply parameters for the message that is being sent. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`message_id` is equal. + + .. versionadded:: 20.8 + + Args: + message_id (:obj:`int`): Identifier of the message that will be replied to in the current + chat, or in the chat :paramref:`chat_id` if it is specified. + chat_id (:obj:`int` | :obj:`str`, optional): If the message to be replied to is from a + different chat, |chat_id_channel| + Not supported for messages sent on behalf of a business account. + allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| Can be + used only for replies in the same chat and forum topic. + quote (:obj:`str`, optional): Quoted part of the message to be replied to; 0-1024 + characters after entities parsing. The quote must be an exact substring of the message + to be replied to, including bold, italic, underline, strikethrough, spoiler, and + custom_emoji entities. The message will fail to send if the quote isn't found in the + original message. + quote_parse_mode (:obj:`str`, optional): Mode for parsing entities in the quote. See + :wiki:`formatting options ` for + more details. + quote_entities (Sequence[:class:`telegram.MessageEntity`], optional): A JSON-serialized + list + of special entities that appear in the quote. It can be specified instead of + :paramref:`quote_parse_mode`. + quote_position (:obj:`int`, optional): Position of the quote in the original message in + UTF-16 code units. + + Attributes: + message_id (:obj:`int`): Identifier of the message that will be replied to in the current + chat, or in the chat :paramref:`chat_id` if it is specified. + chat_id (:obj:`int` | :obj:`str`): Optional. If the message to be replied to is from a + different chat, |chat_id_channel| + Not supported for messages sent on behalf of a business account. + allow_sending_without_reply (:obj:`bool`): Optional. |allow_sending_without_reply| Can be + used only for replies in the same chat and forum topic. + quote (:obj:`str`): Optional. Quoted part of the message to be replied to; 0-1024 + characters after entities parsing. The quote must be an exact substring of the message + to be replied to, including bold, italic, underline, strikethrough, spoiler, and + custom_emoji entities. The message will fail to send if the quote isn't found in the + original message. + quote_parse_mode (:obj:`str`): Optional. Mode for parsing entities in the quote. See + :wiki:`formatting options ` for + more details. + quote_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. A JSON-serialized list + of special entities that appear in the quote. It can be specified instead of + :paramref:`quote_parse_mode`. + quote_position (:obj:`int`): Optional. Position of the quote in the original message in + UTF-16 code units. + """ + + __slots__ = ( + "allow_sending_without_reply", + "chat_id", + "message_id", + "quote", + "quote_entities", + "quote_parse_mode", + "quote_position", + ) + + def __init__( + self, + message_id: int, + chat_id: Optional[Union[int, str]] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + quote: Optional[str] = None, + quote_parse_mode: ODVInput[str] = DEFAULT_NONE, + quote_entities: Optional[Sequence[MessageEntity]] = None, + quote_position: Optional[int] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + self.message_id: int = message_id + self.chat_id: Optional[Union[int, str]] = chat_id + self.allow_sending_without_reply: ODVInput[bool] = allow_sending_without_reply + self.quote: Optional[str] = quote + self.quote_parse_mode: ODVInput[str] = quote_parse_mode + self.quote_entities: Optional[Tuple[MessageEntity, ...]] = parse_sequence_arg( + quote_entities + ) + self.quote_position: Optional[int] = quote_position + + self._id_attrs = (self.message_id,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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)) + + return super().de_json(data=data, bot=bot) diff --git a/_replykeyboardmarkup.py b/_replykeyboardmarkup.py new file mode 100644 index 0000000000000000000000000000000000000000..1b410ebc709b736b049fc8aa342740301aa8b919 --- /dev/null +++ b/_replykeyboardmarkup.py @@ -0,0 +1,359 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram ReplyKeyboardMarkup.""" + +from typing import Final, Optional, Sequence, Tuple, Union + +from telegram import constants +from telegram._keyboardbutton import KeyboardButton +from telegram._telegramobject import TelegramObject +from telegram._utils.markup import check_keyboard_type +from telegram._utils.types import JSONDict + + +class ReplyKeyboardMarkup(TelegramObject): + """This object represents a custom keyboard with reply options. Not supported in channels and + for messages sent on behalf of a Telegram Business account. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their size of :attr:`keyboard` and all the buttons are equal. + + .. figure:: https://core.telegram.org/file/464001950/1191a/2RwpmgU-swU.123554/b5\ + 0478c124d5914c23 + :align: center + + A reply keyboard with reply options. + + .. seealso:: + Another kind of keyboard would be the :class:`telegram.InlineKeyboardMarkup`. + + Examples: + * Example usage: A user requests to change the bot's language, bot replies to the request + with a keyboard to select the new language. Other users in the group don't see + the keyboard. + * :any:`Conversation Bot ` + * :any:`Conversation Bot 2 ` + + Args: + keyboard (Sequence[Sequence[:obj:`str` | :class:`telegram.KeyboardButton`]]): Array of + button rows, each represented by an Array of :class:`telegram.KeyboardButton` objects. + resize_keyboard (:obj:`bool`, optional): Requests clients to resize the keyboard vertically + for optimal fit (e.g., make the keyboard smaller if there are just two rows of + buttons). Defaults to :obj:`False`, in which case the custom keyboard is always of the + same height as the app's standard keyboard. + one_time_keyboard (:obj:`bool`, optional): Requests clients to hide the keyboard as soon as + it's been used. The keyboard will still be available, but clients will automatically + display the usual letter-keyboard in the chat - the user can press a special button in + the input field to see the custom keyboard again. Defaults to :obj:`False`. + selective (:obj:`bool`, optional): Use this parameter if you want to show the keyboard to + specific users only. Targets: + + 1) Users that are @mentioned in the :attr:`~telegram.Message.text` of the + :class:`telegram.Message` object. + 2) If the bot's message is a reply to a message in the same chat and forum topic, + sender of the original message. + + Defaults to :obj:`False`. + + input_field_placeholder (:obj:`str`, optional): The placeholder to be shown in the input + field when the keyboard is active; + :tg-const:`telegram.ReplyKeyboardMarkup.MIN_INPUT_FIELD_PLACEHOLDER`- + :tg-const:`telegram.ReplyKeyboardMarkup.MAX_INPUT_FIELD_PLACEHOLDER` + characters. + + .. versionadded:: 13.7 + is_persistent (:obj:`bool`, optional): Requests clients to always show the keyboard when + the regular keyboard is hidden. Defaults to :obj:`False`, in which case the custom + keyboard can be hidden and opened with a keyboard icon. + + .. versionadded:: 20.0 + + Attributes: + keyboard (Tuple[Tuple[:class:`telegram.KeyboardButton`]]): Array of button rows, + each represented by an Array of :class:`telegram.KeyboardButton` objects. + resize_keyboard (:obj:`bool`): Optional. Requests clients to resize the keyboard vertically + for optimal fit (e.g., make the keyboard smaller if there are just two rows of + buttons). Defaults to :obj:`False`, in which case the custom keyboard is always of the + same height as the app's standard keyboard. + one_time_keyboard (:obj:`bool`): Optional. Requests clients to hide the keyboard as soon as + it's been used. The keyboard will still be available, but clients will automatically + display the usual letter-keyboard in the chat - the user can press a special button in + the input field to see the custom keyboard again. Defaults to :obj:`False`. + selective (:obj:`bool`): Optional. Show the keyboard to specific users only. + Targets: + + 1) Users that are @mentioned in the :attr:`~telegram.Message.text` of the + :class:`telegram.Message` object. + 2) If the bot's message is a reply to a message in the same chat and forum topic, + sender of the original message. + + Defaults to :obj:`False`. + + input_field_placeholder (:obj:`str`): Optional. The placeholder to be shown in the input + field when the keyboard is active; + :tg-const:`telegram.ReplyKeyboardMarkup.MIN_INPUT_FIELD_PLACEHOLDER`- + :tg-const:`telegram.ReplyKeyboardMarkup.MAX_INPUT_FIELD_PLACEHOLDER` + characters. + + .. versionadded:: 13.7 + is_persistent (:obj:`bool`): Optional. Requests clients to always show the keyboard when + the regular keyboard is hidden. If :obj:`False`, the custom keyboard can be hidden and + opened with a keyboard icon. + + .. versionadded:: 20.0 + + """ + + __slots__ = ( + "input_field_placeholder", + "is_persistent", + "keyboard", + "one_time_keyboard", + "resize_keyboard", + "selective", + ) + + def __init__( + self, + keyboard: Sequence[Sequence[Union[str, KeyboardButton]]], + resize_keyboard: Optional[bool] = None, + one_time_keyboard: Optional[bool] = None, + selective: Optional[bool] = None, + input_field_placeholder: Optional[str] = None, + is_persistent: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + if not check_keyboard_type(keyboard): + raise ValueError( + "The parameter `keyboard` should be a sequence of sequences of " + "strings or KeyboardButtons" + ) + + # Required + self.keyboard: Tuple[Tuple[KeyboardButton, ...], ...] = tuple( + tuple(KeyboardButton(button) if isinstance(button, str) else button for button in row) + for row in keyboard + ) + + # Optionals + self.resize_keyboard: Optional[bool] = resize_keyboard + self.one_time_keyboard: Optional[bool] = one_time_keyboard + self.selective: Optional[bool] = selective + self.input_field_placeholder: Optional[str] = input_field_placeholder + self.is_persistent: Optional[bool] = is_persistent + + self._id_attrs = (self.keyboard,) + + self._freeze() + + @classmethod + def from_button( + cls, + button: Union[KeyboardButton, str], + resize_keyboard: bool = False, + one_time_keyboard: bool = False, + selective: bool = False, + input_field_placeholder: Optional[str] = None, + is_persistent: Optional[bool] = None, + **kwargs: object, + ) -> "ReplyKeyboardMarkup": + """Shortcut for:: + + ReplyKeyboardMarkup([[button]], **kwargs) + + Return a ReplyKeyboardMarkup from a single KeyboardButton. + + Args: + button (:class:`telegram.KeyboardButton` | :obj:`str`): The button to use in + the markup. + resize_keyboard (:obj:`bool`, optional): Requests clients to resize the keyboard + vertically for optimal fit (e.g., make the keyboard smaller if there are just two + rows of buttons). Defaults to :obj:`False`, in which case the custom keyboard is + always of the same height as the app's standard keyboard. + one_time_keyboard (:obj:`bool`, optional): Requests clients to hide the keyboard as + soon as it's been used. The keyboard will still be available, but clients will + automatically display the usual letter-keyboard in the chat - the user can press + a special button in the input field to see the custom keyboard again. + Defaults to :obj:`False`. + selective (:obj:`bool`, optional): Use this parameter if you want to show the keyboard + to specific users only. Targets: + + 1) Users that are @mentioned in the text of the Message object. + 2) If the bot's message is a reply to a message in the same chat and forum topic, + sender of the original message. + + Defaults to :obj:`False`. + + input_field_placeholder (:obj:`str`): Optional. The placeholder shown in the input + field when the reply is active. + + .. versionadded:: 13.7 + is_persistent (:obj:`bool`): Optional. Requests clients to always show the keyboard + when the regular keyboard is hidden. Defaults to :obj:`False`, in which case the + custom keyboard can be hidden and opened with a keyboard icon. + + .. versionadded:: 20.0 + """ + return cls( + [[button]], + resize_keyboard=resize_keyboard, + one_time_keyboard=one_time_keyboard, + selective=selective, + input_field_placeholder=input_field_placeholder, + is_persistent=is_persistent, + **kwargs, # type: ignore[arg-type] + ) + + @classmethod + def from_row( + cls, + button_row: Sequence[Union[str, KeyboardButton]], + resize_keyboard: bool = False, + one_time_keyboard: bool = False, + selective: bool = False, + input_field_placeholder: Optional[str] = None, + is_persistent: Optional[bool] = None, + **kwargs: object, + ) -> "ReplyKeyboardMarkup": + """Shortcut for:: + + ReplyKeyboardMarkup([button_row], **kwargs) + + Return a ReplyKeyboardMarkup from a single row of KeyboardButtons. + + Args: + button_row (Sequence[:class:`telegram.KeyboardButton` | :obj:`str`]): The button to + use in the markup. + + .. versionchanged:: 20.0 + |sequenceargs| + resize_keyboard (:obj:`bool`, optional): Requests clients to resize the keyboard + vertically for optimal fit (e.g., make the keyboard smaller if there are just two + rows of buttons). Defaults to :obj:`False`, in which case the custom keyboard is + always of the same height as the app's standard keyboard. + one_time_keyboard (:obj:`bool`, optional): Requests clients to hide the keyboard as + soon as it's been used. The keyboard will still be available, but clients will + automatically display the usual letter-keyboard in the chat - the user can press + a special button in the input field to see the custom keyboard again. + Defaults to :obj:`False`. + selective (:obj:`bool`, optional): Use this parameter if you want to show the keyboard + to specific users only. Targets: + + 1) Users that are @mentioned in the text of the Message object. + 2) If the bot's message is a reply to a message in the same chat and forum topic, + sender of the original message. + + Defaults to :obj:`False`. + + input_field_placeholder (:obj:`str`): Optional. The placeholder shown in the input + field when the reply is active. + + .. versionadded:: 13.7 + is_persistent (:obj:`bool`): Optional. Requests clients to always show the keyboard + when the regular keyboard is hidden. Defaults to :obj:`False`, in which case the + custom keyboard can be hidden and opened with a keyboard icon. + + .. versionadded:: 20.0 + + """ + return cls( + [button_row], + resize_keyboard=resize_keyboard, + one_time_keyboard=one_time_keyboard, + selective=selective, + input_field_placeholder=input_field_placeholder, + is_persistent=is_persistent, + **kwargs, # type: ignore[arg-type] + ) + + @classmethod + def from_column( + cls, + button_column: Sequence[Union[str, KeyboardButton]], + resize_keyboard: bool = False, + one_time_keyboard: bool = False, + selective: bool = False, + input_field_placeholder: Optional[str] = None, + is_persistent: Optional[bool] = None, + **kwargs: object, + ) -> "ReplyKeyboardMarkup": + """Shortcut for:: + + ReplyKeyboardMarkup([[button] for button in button_column], **kwargs) + + Return a ReplyKeyboardMarkup from a single column of KeyboardButtons. + + Args: + button_column (Sequence[:class:`telegram.KeyboardButton` | :obj:`str`]): The button + to use in the markup. + + .. versionchanged:: 20.0 + |sequenceargs| + resize_keyboard (:obj:`bool`, optional): Requests clients to resize the keyboard + vertically for optimal fit (e.g., make the keyboard smaller if there are just two + rows of buttons). Defaults to :obj:`False`, in which case the custom keyboard is + always of the same height as the app's standard keyboard. + one_time_keyboard (:obj:`bool`, optional): Requests clients to hide the keyboard as + soon as it's been used. The keyboard will still be available, but clients will + automatically display the usual letter-keyboard in the chat - the user can press + a special button in the input field to see the custom keyboard again. + Defaults to :obj:`False`. + selective (:obj:`bool`, optional): Use this parameter if you want to show the keyboard + to specific users only. Targets: + + 1) Users that are @mentioned in the text of the Message object. + 2) If the bot's message is a reply to a message in the same chat and forum topic, + sender of the original message. + + Defaults to :obj:`False`. + + input_field_placeholder (:obj:`str`): Optional. The placeholder shown in the input + field when the reply is active. + + .. versionadded:: 13.7 + is_persistent (:obj:`bool`): Optional. Requests clients to always show the keyboard + when the regular keyboard is hidden. Defaults to :obj:`False`, in which case the + custom keyboard can be hidden and opened with a keyboard icon. + + .. versionadded:: 20.0 + + """ + button_grid = [[button] for button in button_column] + return cls( + button_grid, + resize_keyboard=resize_keyboard, + one_time_keyboard=one_time_keyboard, + selective=selective, + input_field_placeholder=input_field_placeholder, + is_persistent=is_persistent, + **kwargs, # type: ignore[arg-type] + ) + + MIN_INPUT_FIELD_PLACEHOLDER: Final[int] = constants.ReplyLimit.MIN_INPUT_FIELD_PLACEHOLDER + """:const:`telegram.constants.ReplyLimit.MIN_INPUT_FIELD_PLACEHOLDER` + + .. versionadded:: 20.0 + """ + MAX_INPUT_FIELD_PLACEHOLDER: Final[int] = constants.ReplyLimit.MAX_INPUT_FIELD_PLACEHOLDER + """:const:`telegram.constants.ReplyLimit.MAX_INPUT_FIELD_PLACEHOLDER` + + .. versionadded:: 20.0 + """ diff --git a/_replykeyboardremove.py b/_replykeyboardremove.py new file mode 100644 index 0000000000000000000000000000000000000000..6cd1a649f4ee02b9dfc2a0923e02b0c7043a3892 --- /dev/null +++ b/_replykeyboardremove.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram ReplyKeyboardRemove.""" +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class ReplyKeyboardRemove(TelegramObject): + """ + Upon receiving a message with this object, Telegram clients will remove the current custom + keyboard and display the default letter-keyboard. By default, custom keyboards are displayed + until a new keyboard is sent by a bot. An exception is made for one-time keyboards that are + hidden immediately after the user presses a button (see :class:`telegram.ReplyKeyboardMarkup`). + Not supported in channels and for messages sent on behalf of a Telegram Business account. + + Note: + User will not be able to summon this keyboard; if you want to hide the keyboard from + sight but keep it accessible, use :attr:`telegram.ReplyKeyboardMarkup.one_time_keyboard`. + + Examples: + * Example usage: A user votes in a poll, bot returns confirmation message in reply to + the vote and removes the keyboard for that user, while still showing the keyboard with + poll options to users who haven't voted yet. + * :any:`Conversation Bot ` + * :any:`Conversation Bot 2 ` + + Args: + selective (:obj:`bool`, optional): Use this parameter if you want to remove the keyboard + for specific users only. Targets: + + 1) Users that are @mentioned in the text of the :class:`telegram.Message` object. + 2) If the bot's message is a reply to a message in the same chat and forum topic, + sender of the original message. + + Attributes: + remove_keyboard (:obj:`True`): Requests clients to remove the custom keyboard. + selective (:obj:`bool`): Optional. Remove the keyboard for specific users only. + Targets: + + 1) Users that are @mentioned in the text of the :class:`telegram.Message` object. + 2) If the bot's message is a reply to a message in the same chat and forum topic, + sender of the original message. + + """ + + __slots__ = ("remove_keyboard", "selective") + + def __init__(self, selective: Optional[bool] = None, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + # Required + self.remove_keyboard: bool = True + # Optionals + self.selective: Optional[bool] = selective + + self._freeze() diff --git a/_sentwebappmessage.py b/_sentwebappmessage.py new file mode 100644 index 0000000000000000000000000000000000000000..28ae55f7516cf917b129ef54c49855022454f101 --- /dev/null +++ b/_sentwebappmessage.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram Sent Web App Message.""" +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class SentWebAppMessage(TelegramObject): + """Contains information about an inline message sent by a Web App on behalf of a user. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`inline_message_id` are equal. + + .. versionadded:: 20.0 + + Args: + inline_message_id (:obj:`str`, optional): Identifier of the sent inline message. Available + only if there is an :attr:`inline keyboard ` attached to + the message. + + Attributes: + inline_message_id (:obj:`str`): Optional. Identifier of the sent inline message. Available + only if there is an :attr:`inline keyboard ` attached to + the message. + """ + + __slots__ = ("inline_message_id",) + + def __init__( + self, inline_message_id: Optional[str] = None, *, api_kwargs: Optional[JSONDict] = None + ): + super().__init__(api_kwargs=api_kwargs) + # Optionals + self.inline_message_id: Optional[str] = inline_message_id + + self._id_attrs = (self.inline_message_id,) + + self._freeze() diff --git a/_shared.py b/_shared.py new file mode 100644 index 0000000000000000000000000000000000000000..b4ce2c4d5a0df2c86db12d259f7d7869f656daa5 --- /dev/null +++ b/_shared.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains two objects used for request chats/users service messages.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._files.photosize import PhotoSize +from telegram._telegramobject import TelegramObject +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram._bot import Bot + + +class UsersShared(TelegramObject): + """ + This object contains information about the user whose identifier was shared with the bot + using a :class:`telegram.KeyboardButtonRequestUsers` button. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`request_id` and :attr:`users` are equal. + + .. versionadded:: 20.8 + Bot API 7.0 replaces ``UserShared`` with this class. The only difference is that now + the ``user_ids`` is a sequence instead of a single integer. + + .. versionchanged:: 21.1 + The argument :attr:`users` is now considered for the equality comparison instead of + ``user_ids``. + + .. versionremoved:: 21.2 + Removed the deprecated argument and attribute ``user_ids``. + + Args: + request_id (:obj:`int`): Identifier of the request. + users (Sequence[:class:`telegram.SharedUser`]): Information about users shared with the + bot. + + .. versionadded:: 21.1 + + .. versionchanged:: 21.2 + This argument is now required. + + Attributes: + request_id (:obj:`int`): Identifier of the request. + users (Tuple[:class:`telegram.SharedUser`]): Information about users shared with the + bot. + + .. versionadded:: 21.1 + """ + + __slots__ = ("request_id", "users") + + def __init__( + self, + request_id: int, + users: Sequence["SharedUser"], + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.request_id: int = request_id + self.users: Tuple[SharedUser, ...] = parse_sequence_arg(users) + + self._id_attrs = (self.request_id, self.users) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + 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 + if user_ids := data.get("user_ids"): + api_kwargs = {"user_ids": user_ids} + + return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs) + + +class ChatShared(TelegramObject): + """ + This object contains information about the chat whose identifier was shared with the bot + using a :class:`telegram.KeyboardButtonRequestChat` button. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`request_id` and :attr:`chat_id` are equal. + + .. versionadded:: 20.1 + + Args: + request_id (:obj:`int`): Identifier of the request. + chat_id (:obj:`int`): Identifier of the shared user. This number may be greater than 32 + bits and some programming languages may have difficulty/silent defects in interpreting + it. But it is smaller than 52 bits, so a signed 64-bit integer or double-precision + float type are safe for storing this identifier. + title (:obj:`str`, optional): Title of the chat, if the title was requested by the bot. + + .. versionadded:: 21.1 + username (:obj:`str`, optional): Username of the chat, if the username was requested by + the bot and available. + + .. versionadded:: 21.1 + photo (Sequence[:class:`telegram.PhotoSize`], optional): Available sizes of the chat photo, + if the photo was requested by the bot + + .. versionadded:: 21.1 + + Attributes: + request_id (:obj:`int`): Identifier of the request. + chat_id (:obj:`int`): Identifier of the shared user. This number may be greater than 32 + bits and some programming languages may have difficulty/silent defects in interpreting + it. But it is smaller than 52 bits, so a signed 64-bit integer or double-precision + float type are safe for storing this identifier. + title (:obj:`str`): Optional. Title of the chat, if the title was requested by the bot. + + .. versionadded:: 21.1 + username (:obj:`str`): Optional. Username of the chat, if the username was requested by + the bot and available. + + .. versionadded:: 21.1 + photo (Tuple[:class:`telegram.PhotoSize`]): Optional. Available sizes of the chat photo, + if the photo was requested by the bot + + .. versionadded:: 21.1 + """ + + __slots__ = ("chat_id", "photo", "request_id", "title", "username") + + def __init__( + self, + request_id: int, + chat_id: int, + title: Optional[str] = None, + username: Optional[str] = None, + photo: Optional[Sequence[PhotoSize]] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.request_id: int = request_id + self.chat_id: int = chat_id + self.title: Optional[str] = title + self.username: Optional[str] = username + self.photo: Optional[Tuple[PhotoSize, ...]] = parse_sequence_arg(photo) + + self._id_attrs = (self.request_id, self.chat_id) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + return super().de_json(data=data, bot=bot) + + +class SharedUser(TelegramObject): + """ + This object contains information about a user that was shared with the bot using a + :class:`telegram.KeyboardButtonRequestUsers` button. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`user_id` is equal. + + .. versionadded:: 21.1 + + Args: + user_id (:obj:`int`): Identifier of the shared user. This number may have 32 significant + bits and some programming languages may have difficulty/silent defects in interpreting + it. But it has atmost 52 significant bits, so 64-bit integers or double-precision + float types are safe for storing these identifiers. The bot may not have access to the + user and could be unable to use this identifier, unless the user is already known to + the bot by some other means. + first_name (:obj:`str`, optional): First name of the user, if the name was requested by the + bot. + last_name (:obj:`str`, optional): Last name of the user, if the name was requested by the + bot. + username (:obj:`str`, optional): Username of the user, if the username was requested by the + bot. + photo (Sequence[:class:`telegram.PhotoSize`], optional): Available sizes of the chat photo, + if the photo was requested by the bot. + + Attributes: + user_id (:obj:`int`): Identifier of the shared user. This number may have 32 significant + bits and some programming languages may have difficulty/silent defects in interpreting + it. But it has atmost 52 significant bits, so 64-bit integers or double-precision + float types are safe for storing these identifiers. The bot may not have access to the + user and could be unable to use this identifier, unless the user is already known to + the bot by some other means. + first_name (:obj:`str`): Optional. First name of the user, if the name was requested by the + bot. + last_name (:obj:`str`): Optional. Last name of the user, if the name was requested by the + bot. + username (:obj:`str`): Optional. Username of the user, if the username was requested by the + bot. + photo (Tuple[:class:`telegram.PhotoSize`]): Available sizes of the chat photo, if + the photo was requested by the bot. This list is empty if the photo was not requsted. + """ + + __slots__ = ("first_name", "last_name", "photo", "user_id", "username") + + def __init__( + self, + user_id: int, + first_name: Optional[str] = None, + last_name: Optional[str] = None, + username: Optional[str] = None, + photo: Optional[Sequence[PhotoSize]] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.user_id: int = user_id + self.first_name: Optional[str] = first_name + self.last_name: Optional[str] = last_name + self.username: Optional[str] = username + self.photo: Optional[Tuple[PhotoSize, ...]] = parse_sequence_arg(photo) + + self._id_attrs = (self.user_id,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + return super().de_json(data=data, bot=bot) diff --git a/_story.py b/_story.py new file mode 100644 index 0000000000000000000000000000000000000000..40d17cdb16d67395da5f631398a0ac0deea1c3c1 --- /dev/null +++ b/_story.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object related to a Telegram Story.""" + +from typing import TYPE_CHECKING, Optional + +from telegram._chat import Chat +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class Story(TelegramObject): + """ + This object represents a story. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`chat` and :attr:`id` are equal. + + .. versionadded:: 20.5 + + .. versionchanged:: 21.0 + Added attributes :attr:`chat` and :attr:`id` and equality based on them. + + Args: + chat (:class:`telegram.Chat`): Chat that posted the story. + id (:obj:`int`): Unique identifier for the story in the chat. + + Attributes: + chat (:class:`telegram.Chat`): Chat that posted the story. + id (:obj:`int`): Unique identifier for the story in the chat. + + """ + + __slots__ = ( + "chat", + "id", + ) + + def __init__( + self, + chat: Chat, + id: int, # pylint: disable=redefined-builtin + *, + api_kwargs: Optional[JSONDict] = None, + ) -> None: + super().__init__(api_kwargs=api_kwargs) + self.chat: Chat = chat + self.id: int = id + + self._id_attrs = (self.chat, self.id) + + self._freeze() + + @classmethod + def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["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) diff --git a/_switchinlinequerychosenchat.py b/_switchinlinequerychosenchat.py new file mode 100644 index 0000000000000000000000000000000000000000..631dbd6798a5ce382da82d051aaefdcff1d362a8 --- /dev/null +++ b/_switchinlinequerychosenchat.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +"""This module contains a class that represents a Telegram SwitchInlineQueryChosenChat.""" +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class SwitchInlineQueryChosenChat(TelegramObject): + """ + This object represents an inline button that switches the current user to inline mode in a + chosen chat, with an optional default inline query. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`query`, :attr:`allow_user_chats`, :attr:`allow_bot_chats`, + :attr:`allow_group_chats`, and :attr:`allow_channel_chats` are equal. + + .. versionadded:: 20.3 + + Caution: + The PTB team has discovered that you must pass at least one of + :paramref:`allow_user_chats`, :paramref:`allow_bot_chats`, :paramref:`allow_group_chats`, + or :paramref:`allow_channel_chats` to Telegram. Otherwise, an error will be raised. + + Args: + query (:obj:`str`, optional): The default inline query to be inserted in the input field. + If left empty, only the bot's username will be inserted. + allow_user_chats (:obj:`bool`, optional): Pass :obj:`True`, if private chats with users + can be chosen. + allow_bot_chats (:obj:`bool`, optional): Pass :obj:`True`, if private chats with bots can + be chosen. + allow_group_chats (:obj:`bool`, optional): Pass :obj:`True`, if group and supergroup chats + can be chosen. + allow_channel_chats (:obj:`bool`, optional): Pass :obj:`True`, if channel chats can be + chosen. + + Attributes: + query (:obj:`str`): Optional. The default inline query to be inserted in the input field. + If left empty, only the bot's username will be inserted. + allow_user_chats (:obj:`bool`): Optional. :obj:`True`, if private chats with users can be + chosen. + allow_bot_chats (:obj:`bool`): Optional. :obj:`True`, if private chats with bots can be + chosen. + allow_group_chats (:obj:`bool`): Optional. :obj:`True`, if group and supergroup chats can + be chosen. + allow_channel_chats (:obj:`bool`): Optional. :obj:`True`, if channel chats can be chosen. + + """ + + __slots__ = ( + "allow_bot_chats", + "allow_channel_chats", + "allow_group_chats", + "allow_user_chats", + "query", + ) + + def __init__( + self, + query: Optional[str] = None, + allow_user_chats: Optional[bool] = None, + allow_bot_chats: Optional[bool] = None, + allow_group_chats: Optional[bool] = None, + allow_channel_chats: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Optional + self.query: Optional[str] = query + self.allow_user_chats: Optional[bool] = allow_user_chats + self.allow_bot_chats: Optional[bool] = allow_bot_chats + self.allow_group_chats: Optional[bool] = allow_group_chats + self.allow_channel_chats: Optional[bool] = allow_channel_chats + + self._id_attrs = ( + self.query, + self.allow_user_chats, + self.allow_bot_chats, + self.allow_group_chats, + self.allow_channel_chats, + ) + + self._freeze() diff --git a/_telegramobject.py b/_telegramobject.py new file mode 100644 index 0000000000000000000000000000000000000000..5040755322628ed77aaba84641cc490c7ce598a6 --- /dev/null +++ b/_telegramobject.py @@ -0,0 +1,688 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""Base class for Telegram Objects.""" +import contextlib +import datetime +import inspect +import json +from collections.abc import Sized +from contextlib import contextmanager +from copy import deepcopy +from itertools import chain +from types import MappingProxyType +from typing import ( + TYPE_CHECKING, + Any, + ClassVar, + Dict, + Iterator, + List, + Mapping, + Optional, + Set, + Tuple, + Type, + TypeVar, + Union, + cast, +) + +from telegram._utils.datetime import to_timestamp +from telegram._utils.defaultvalue import DefaultValue +from telegram._utils.types import JSONDict +from telegram._utils.warnings import warn + +if TYPE_CHECKING: + from telegram import Bot + +Tele_co = TypeVar("Tele_co", bound="TelegramObject", covariant=True) + + +class TelegramObject: + """Base class for most Telegram objects. + + Objects of this type are subscriptable with strings. See :meth:`__getitem__` for more details. + The :mod:`pickle` and :func:`~copy.deepcopy` behavior of objects of this type are defined by + :meth:`__getstate__`, :meth:`__setstate__` and :meth:`__deepcopy__`. + + Tip: + Objects of this type can be serialized via Python's :mod:`pickle` module and pickled + objects from one version of PTB are usually loadable in future versions. However, we can + not guarantee that this compatibility will always be provided. At least a manual one-time + conversion of the data may be needed on major updates of the library. + + .. versionchanged:: 20.0 + + * Removed argument and attribute ``bot`` for several subclasses. Use + :meth:`set_bot` and :meth:`get_bot` instead. + * Removed the possibility to pass arbitrary keyword arguments for several subclasses. + * String representations objects of this type was overhauled. See :meth:`__repr__` for + details. As this class doesn't implement :meth:`object.__str__`, the default + implementation will be used, which is equivalent to :meth:`__repr__`. + * Objects of this class (or subclasses) are now immutable. This means that you can't set + or delete attributes anymore. Moreover, attributes that were formerly of type + :obj:`list` are now of type :obj:`tuple`. + + Arguments: + api_kwargs (Dict[:obj:`str`, any], optional): |toapikwargsarg| + + .. versionadded:: 20.0 + + Attributes: + api_kwargs (:obj:`types.MappingProxyType` [:obj:`str`, any]): |toapikwargsattr| + + .. versionadded:: 20.0 + + """ + + __slots__ = ("_bot", "_frozen", "_id_attrs", "api_kwargs") + + # Used to cache the names of the parameters of the __init__ method of the class + # Must be a private attribute to avoid name clashes between subclasses + __INIT_PARAMS: ClassVar[Set[str]] = set() + # Used to check if __INIT_PARAMS has been set for the current class. Unfortunately, we can't + # just check if `__INIT_PARAMS is None`, since subclasses use the parent class' __INIT_PARAMS + # unless it's overridden + __INIT_PARAMS_CHECK: Optional[Type["TelegramObject"]] = None + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None: + # Setting _frozen to `False` here means that classes without arguments still need to + # implement __init__. However, with `True` would mean increased usage of + # `with self._unfrozen()` in the `__init__` of subclasses and we have fewer empty + # classes than classes with arguments. + self._frozen: bool = False + self._id_attrs: Tuple[object, ...] = () + self._bot: Optional[Bot] = None + # We don't do anything with api_kwargs here - see docstring of _apply_api_kwargs + self.api_kwargs: Mapping[str, Any] = MappingProxyType(api_kwargs or {}) + + def __eq__(self, other: object) -> bool: + """Compares this object with :paramref:`other` in terms of equality. + If this object and :paramref:`other` are `not` objects of the same class, + this comparison will fall back to Python's default implementation of :meth:`object.__eq__`. + Otherwise, both objects may be compared in terms of equality, if the corresponding + subclass of :class:`TelegramObject` has defined a set of attributes to compare and + the objects are considered to be equal, if all of these attributes are equal. + If the subclass has not defined a set of attributes to compare, a warning will be issued. + + Tip: + If instances of a class in the :mod:`telegram` module are comparable in terms of + equality, the documentation of the class will state the attributes that will be used + for this comparison. + + Args: + other (:obj:`object`): The object to compare with. + + Returns: + :obj:`bool` + + """ + if isinstance(other, self.__class__): + if not self._id_attrs: + warn( + f"Objects of type {self.__class__.__name__} can not be meaningfully tested for" + " equivalence.", + stacklevel=2, + ) + if not other._id_attrs: + warn( + f"Objects of type {other.__class__.__name__} can not be meaningfully tested" + " for equivalence.", + stacklevel=2, + ) + return self._id_attrs == other._id_attrs + return super().__eq__(other) + + def __hash__(self) -> int: + """Builds a hash value for this object such that the hash of two objects is equal if and + only if the objects are equal in terms of :meth:`__eq__`. + + Returns: + :obj:`int` + """ + if self._id_attrs: + return hash((self.__class__, self._id_attrs)) + return super().__hash__() + + def __setattr__(self, key: str, value: object) -> None: + """Overrides :meth:`object.__setattr__` to prevent the overriding of attributes. + + Raises: + :exc:`AttributeError` + """ + # protected attributes can always be set for convenient internal use + if key[0] == "_" or not getattr(self, "_frozen", True): + super().__setattr__(key, value) + return + + raise AttributeError( + f"Attribute `{key}` of class `{self.__class__.__name__}` can't be set!" + ) + + def __delattr__(self, key: str) -> None: + """Overrides :meth:`object.__delattr__` to prevent the deletion of attributes. + + Raises: + :exc:`AttributeError` + """ + # protected attributes can always be set for convenient internal use + if key[0] == "_" or not getattr(self, "_frozen", True): + super().__delattr__(key) + return + + raise AttributeError( + f"Attribute `{key}` of class `{self.__class__.__name__}` can't be deleted!" + ) + + def __repr__(self) -> str: + """Gives a string representation of this object in the form + ``ClassName(attr_1=value_1, attr_2=value_2, ...)``, where attributes are omitted if they + have the value :obj:`None` or are empty instances of :class:`collections.abc.Sized` (e.g. + :class:`list`, :class:`dict`, :class:`set`, :class:`str`, etc.). + + As this class doesn't implement :meth:`object.__str__`, the default implementation + will be used, which is equivalent to :meth:`__repr__`. + + Returns: + :obj:`str` + """ + # * `__repr__` goal is to be unambiguous + # * `__str__` goal is to be readable + # * `str()` calls `__repr__`, if `__str__` is not defined + # In our case "unambiguous" and "readable" largely coincide, so we can use the same logic. + as_dict = self._get_attrs(recursive=False, include_private=False) + + if not self.api_kwargs: + # Drop api_kwargs from the representation, if empty + as_dict.pop("api_kwargs", None) + else: + # Otherwise, we want to skip the "mappingproxy" part of the repr + as_dict["api_kwargs"] = dict(self.api_kwargs) + + contents = ", ".join( + f"{k}={as_dict[k]!r}" + for k in sorted(as_dict.keys()) + if ( + as_dict[k] is not None + and not ( + isinstance(as_dict[k], Sized) + and len(as_dict[k]) == 0 # type: ignore[arg-type] + ) + ) + ) + return f"{self.__class__.__name__}({contents})" + + def __getitem__(self, item: str) -> object: + """ + Objects of this type are subscriptable with strings, where + ``telegram_object["attribute_name"]`` is equivalent to ``telegram_object.attribute_name``. + + Tip: + This is useful for dynamic attribute lookup, i.e. ``telegram_object[arg]`` where the + value of ``arg`` is determined at runtime. + In all other cases, it's recommended to use the dot notation instead, i.e. + ``telegram_object.attribute_name``. + + .. versionchanged:: 20.0 + + ``telegram_object['from']`` will look up the key ``from_user``. This is to account for + special cases like :attr:`Message.from_user` that deviate from the official Bot API. + + Args: + item (:obj:`str`): The name of the attribute to look up. + + Returns: + :obj:`object` + + Raises: + :exc:`KeyError`: If the object does not have an attribute with the appropriate name. + """ + if item == "from": + item = "from_user" + try: + return getattr(self, item) + except AttributeError as exc: + raise KeyError( + f"Objects of type {self.__class__.__name__} don't have an attribute called " + f"`{item}`." + ) from exc + + def __getstate__(self) -> Dict[str, Union[str, object]]: + """ + Overrides :meth:`object.__getstate__` to customize the pickling process of objects of this + type. + The returned state does `not` contain the :class:`telegram.Bot` instance set with + :meth:`set_bot` (if any), as it can't be pickled. + + Returns: + state (Dict[:obj:`str`, :obj:`object`]): The state of the object. + """ + out = self._get_attrs( + include_private=True, recursive=False, remove_bot=True, convert_default_vault=False + ) + # MappingProxyType is not pickable, so we convert it to a dict and revert in + # __setstate__ + out["api_kwargs"] = dict(self.api_kwargs) + return out + + def __setstate__(self, state: Dict[str, object]) -> None: + """ + Overrides :meth:`object.__setstate__` to customize the unpickling process of objects of + this type. Modifies the object in-place. + + If any data was stored in the :attr:`api_kwargs` of the pickled object, this method checks + if the class now has dedicated attributes for those keys and moves the values from + :attr:`api_kwargs` to the dedicated attributes. + This can happen, if serialized data is loaded with a new version of this library, where + the new version was updated to account for updates of the Telegram Bot API. + + If on the contrary an attribute was removed from the class, the value is not discarded but + made available via :attr:`api_kwargs`. + + Args: + state (:obj:`dict`): The data to set as attributes of this object. + """ + self._unfreeze() + + # Make sure that we have a `_bot` attribute. This is necessary, since __getstate__ omits + # this as Bots are not pickable. + self._bot = None + + # get api_kwargs first because we may need to add entries to it (see try-except below) + api_kwargs = cast(Dict[str, object], state.pop("api_kwargs", {})) + # get _frozen before the loop to avoid setting it to True in the loop + frozen = state.pop("_frozen", False) + + for key, val in state.items(): + try: + setattr(self, key, val) + except AttributeError: + # So an attribute was deprecated and removed from the class. Let's handle this: + # 1) Is the attribute now a property with no setter? Let's check that: + if isinstance(getattr(self.__class__, key, None), property): + # It is, so let's try to set the "private attribute" instead + try: + setattr(self, f"_{key}", val) + # If this fails as well, guess we've completely removed it. Let's add it to + # api_kwargs as fallback + except AttributeError: + api_kwargs[key] = val + + # 2) The attribute is a private attribute, i.e. it went through case 1) in the past + elif key.startswith("_"): + continue # skip adding this to api_kwargs, the attribute is lost forever. + api_kwargs[key] = val # add it to api_kwargs as fallback + + # For api_kwargs we first apply any kwargs that are already attributes of the object + # and then set the rest as MappingProxyType attribute. Converting to MappingProxyType + # is necessary, since __getstate__ converts it to a dict as MPT is not pickable. + self._apply_api_kwargs(api_kwargs) + self.api_kwargs = MappingProxyType(api_kwargs) + + # Apply freezing if necessary + # we .get(…) the setting for backwards compatibility with objects that were pickled + # before the freeze feature was introduced + if frozen: + self._freeze() + + def __deepcopy__(self: Tele_co, memodict: Dict[int, object]) -> Tele_co: + """ + Customizes how :func:`copy.deepcopy` processes objects of this type. + The only difference to the default implementation is that the :class:`telegram.Bot` + instance set via :meth:`set_bot` (if any) is not copied, but shared between the original + and the copy, i.e.:: + + assert telegram_object.get_bot() is copy.deepcopy(telegram_object).get_bot() + + Args: + memodict (:obj:`dict`): A dictionary that maps objects to their copies. + + Returns: + :class:`telegram.TelegramObject`: The copied object. + """ + bot = self._bot # Save bot so we can set it after copying + self.set_bot(None) # set to None so it is not deepcopied + cls = self.__class__ + result = cls.__new__(cls) # create a new instance + memodict[id(self)] = result # save the id of the object in the dict + + result._frozen = False # unfreeze the new object for setting the attributes + + # now we set the attributes in the deepcopied object + for k in self._get_attrs_names(include_private=True): + if k == "_frozen": + # Setting the frozen status to True would prevent the attributes from being set + continue + if k == "api_kwargs": + # Need to copy api_kwargs manually, since it's a MappingProxyType is not + # pickable and deepcopy uses the pickle interface + setattr(result, k, MappingProxyType(deepcopy(dict(self.api_kwargs), memodict))) + continue + + try: + setattr(result, k, deepcopy(getattr(self, k), memodict)) + except AttributeError: + # Skip missing attributes. This can happen if the object was loaded from a pickle + # file that was created with an older version of the library, where the class + # did not have the attribute yet. + continue + + # Apply freezing if necessary + if self._frozen: + result._freeze() + + result.set_bot(bot) # Assign the bots back + self.set_bot(bot) + return result + + @staticmethod + def _parse_data(data: Optional[JSONDict]) -> Optional[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() + + @classmethod + def _de_json( + cls: Type[Tele_co], + data: Optional[JSONDict], + bot: Optional["Bot"], + api_kwargs: Optional[JSONDict] = None, + ) -> Optional[Tele_co]: + if data is None: + return None + + # try-except is significantly faster in case we already have a correct argument set + try: + obj = cls(**data, api_kwargs=api_kwargs) + except TypeError as exc: + if "__init__() got an unexpected keyword argument" not in str(exc): + raise + + if cls.__INIT_PARAMS_CHECK is not cls: + signature = inspect.signature(cls) + cls.__INIT_PARAMS = set(signature.parameters.keys()) + cls.__INIT_PARAMS_CHECK = cls + + api_kwargs = api_kwargs or {} + existing_kwargs: JSONDict = {} + for key, value in data.items(): + (existing_kwargs if key in cls.__INIT_PARAMS else api_kwargs)[key] = value + + obj = cls(api_kwargs=api_kwargs, **existing_kwargs) + + obj.set_bot(bot=bot) + return obj + + @classmethod + def de_json( + cls: Type[Tele_co], data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional[Tele_co]: + """Converts JSON data to a Telegram object. + + Args: + data (Dict[:obj:`str`, ...]): The JSON data. + bot (:class:`telegram.Bot`, optional): The bot associated with this object. Defaults to + :obj:`None`, in which case shortcut methods will not be available. + + .. versionchanged:: 21.4 + :paramref:`bot` is now optional and defaults to :obj:`None` + + Returns: + The Telegram object. + + """ + return cls._de_json(data=data, bot=bot) + + @classmethod + def de_list( + cls: Type[Tele_co], data: Optional[List[JSONDict]], bot: Optional["Bot"] = None + ) -> Tuple[Tele_co, ...]: + """Converts a list of JSON objects to a tuple of Telegram objects. + + .. versionchanged:: 20.0 + + * Returns a tuple instead of a list. + * Filters out any :obj:`None` values. + + Args: + data (List[Dict[:obj:`str`, ...]]): The JSON data. + bot (:class:`telegram.Bot`, optional): The bot associated with these object. Defaults + to :obj:`None`, in which case shortcut methods will not be available. + + .. versionchanged:: 21.4 + :paramref:`bot` is now optional and defaults to :obj:`None` + + Returns: + 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) + + @contextmanager + def _unfrozen(self: Tele_co) -> Iterator[Tele_co]: + """Context manager to temporarily unfreeze the object. For internal use only. + + Note: + with to._unfrozen() as other_to: + assert to is other_to + """ + self._unfreeze() + yield self + self._freeze() + + def _freeze(self) -> None: + self._frozen = True + + def _unfreeze(self) -> None: + self._frozen = False + + def _apply_api_kwargs(self, api_kwargs: JSONDict) -> None: + """Loops through the api kwargs and for every key that exists as attribute of the + object (and is None), it moves the value from `api_kwargs` to the attribute. + *Edits `api_kwargs` in place!* + + This method is currently only called in the unpickling process, i.e. not on "normal" init. + This is because + * automating this is tricky to get right: It should be called at the *end* of the __init__, + preferably only once at the end of the __init__ of the last child class. This could be + done via __init_subclass__, but it's hard to not destroy the signature of __init__ in the + process. + * calling it manually in every __init__ is tedious + * There probably is no use case for it anyway. If you manually initialize a TO subclass, + then you can pass everything as proper argument. + """ + # we convert to list to ensure that the list doesn't change length while we loop + for key in list(api_kwargs.keys()): + # property attributes are not settable, so we need to set the private attribute + if isinstance(getattr(self.__class__, key, None), property): + # if setattr fails, we'll just leave the value in api_kwargs: + with contextlib.suppress(AttributeError): + setattr(self, f"_{key}", api_kwargs.pop(key)) + elif getattr(self, key, True) is None: + setattr(self, key, api_kwargs.pop(key)) + + def _get_attrs_names(self, include_private: bool) -> Iterator[str]: + """ + Returns the names of the attributes of this object. This is used to determine which + attributes should be serialized when pickling the object. + + Args: + include_private (:obj:`bool`): Whether to include private attributes. + + Returns: + Iterator[:obj:`str`]: An iterator over the names of the attributes of this object. + """ + # We want to get all attributes for the class, using self.__slots__ only includes the + # attributes used by that class itself, and not its superclass(es). Hence, we get its MRO + # and then get their attributes. The `[:-1]` slice excludes the `object` class + all_slots = (s for c in self.__class__.__mro__[:-1] for s in c.__slots__) # type: ignore + # chain the class's slots with the user defined subclass __dict__ (class has no slots) + all_attrs = ( + chain(all_slots, self.__dict__.keys()) if hasattr(self, "__dict__") else all_slots + ) + + if include_private: + return all_attrs + return (attr for attr in all_attrs if not attr.startswith("_")) + + def _get_attrs( + self, + include_private: bool = False, + recursive: bool = False, + remove_bot: bool = False, + convert_default_vault: bool = True, + ) -> Dict[str, Union[str, object]]: + """This method is used for obtaining the attributes of the object. + + Args: + include_private (:obj:`bool`): Whether the result should include private variables. + recursive (:obj:`bool`): If :obj:`True`, will convert any ``TelegramObjects`` (if + found) in the attributes to a dictionary. Else, preserves it as an object itself. + remove_bot (:obj:`bool`): Whether the bot should be included in the result. + convert_default_vault (:obj:`bool`): Whether :class:`telegram.DefaultValue` should be + converted to its true value. This is necessary when converting to a dictionary for + end users since DefaultValue is used in some classes that work with + `tg.ext.defaults` (like `LinkPreviewOptions`) + + Returns: + :obj:`dict`: A dict where the keys are attribute names and values are their values. + """ + data = {} + + for key in self._get_attrs_names(include_private=include_private): + value = ( + DefaultValue.get_value(getattr(self, key, None)) + if convert_default_vault + else getattr(self, key, None) + ) + + if value is not None: + if recursive and hasattr(value, "to_dict"): + data[key] = value.to_dict(recursive=True) + else: + data[key] = value + elif not recursive: + data[key] = value + + if recursive and data.get("from_user"): + data["from"] = data.pop("from_user", None) + if remove_bot: + data.pop("_bot", None) + return data + + def to_json(self) -> str: + """Gives a JSON representation of object. + + .. versionchanged:: 20.0 + Now includes all entries of :attr:`api_kwargs`. + + Returns: + :obj:`str` + """ + return json.dumps(self.to_dict()) + + def to_dict(self, recursive: bool = True) -> JSONDict: + """Gives representation of object as :obj:`dict`. + + .. versionchanged:: 20.0 + + * Now includes all entries of :attr:`api_kwargs`. + * Attributes whose values are empty sequences are no longer included. + + Args: + recursive (:obj:`bool`, optional): If :obj:`True`, will convert any TelegramObjects + (if found) in the attributes to a dictionary. Else, preserves it as an object + itself. Defaults to :obj:`True`. + + .. versionadded:: 20.0 + + Returns: + :obj:`dict` + """ + out = self._get_attrs(recursive=recursive) + + # Now we should convert TGObjects to dicts inside objects such as sequences, and convert + # datetimes to timestamps. This mostly eliminates the need for subclasses to override + # `to_dict` + pop_keys: Set[str] = set() + for key, value in out.items(): + if isinstance(value, (tuple, list)): + if not value: + # not popping directly to avoid changing the dict size during iteration + pop_keys.add(key) + continue + + val = [] # empty list to append our converted values to + for item in value: + if hasattr(item, "to_dict"): + val.append(item.to_dict(recursive=recursive)) + # This branch is useful for e.g. Tuple[Tuple[PhotoSize|KeyboardButton]] + elif isinstance(item, (tuple, list)): + val.append( + [ + i.to_dict(recursive=recursive) if hasattr(i, "to_dict") else i + for i in item + ] + ) + else: # if it's not a TGObject, just append it. E.g. [TGObject, 2] + val.append(item) + out[key] = val + + elif isinstance(value, datetime.datetime): + out[key] = to_timestamp(value) + + for key in pop_keys: + out.pop(key) + + # Effectively "unpack" api_kwargs into `out`: + out.update(out.pop("api_kwargs", {})) # type: ignore[call-overload] + return out + + def get_bot(self) -> "Bot": + """Returns the :class:`telegram.Bot` instance associated with this object. + + .. seealso:: :meth:`set_bot` + + .. versionadded: 20.0 + + Raises: + RuntimeError: If no :class:`telegram.Bot` instance was set for this object. + """ + if self._bot is None: + raise RuntimeError( + "This object has no bot associated with it. Shortcuts cannot be used." + ) + return self._bot + + def set_bot(self, bot: Optional["Bot"]) -> None: + """Sets the :class:`telegram.Bot` instance associated with this object. + + .. seealso:: :meth:`get_bot` + + .. versionadded: 20.0 + + Arguments: + bot (:class:`telegram.Bot` | :obj:`None`): The bot instance. + """ + self._bot = bot diff --git a/_update.py b/_update.py new file mode 100644 index 0000000000000000000000000000000000000000..579cb0085809f43c7d905f5e9d5e1e5e13b5be25 --- /dev/null +++ b/_update.py @@ -0,0 +1,772 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram Update.""" + +from typing import TYPE_CHECKING, Final, List, Optional, Union + +from telegram import constants +from telegram._business import BusinessConnection, BusinessMessagesDeleted +from telegram._callbackquery import CallbackQuery +from telegram._chatboost import ChatBoostRemoved, ChatBoostUpdated +from telegram._chatjoinrequest import ChatJoinRequest +from telegram._chatmemberupdated import ChatMemberUpdated +from telegram._choseninlineresult import ChosenInlineResult +from telegram._inline.inlinequery import InlineQuery +from telegram._message import Message +from telegram._messagereactionupdated import MessageReactionCountUpdated, MessageReactionUpdated +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.types import JSONDict +from telegram._utils.warnings import warn + +if TYPE_CHECKING: + from telegram import Bot, Chat, User + + +class Update(TelegramObject): + """This object represents an incoming update. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`update_id` is equal. + + Note: + At most one of the optional parameters can be present in any given update. + + .. seealso:: :wiki:`Your First Bot ` + + Args: + update_id (:obj:`int`): The update's unique identifier. Update identifiers start from a + certain positive number and increase sequentially. This ID becomes especially handy if + you're using Webhooks, since it allows you to ignore repeated updates or to restore the + correct update sequence, should they get out of order. If there are no new updates for + at least a week, then identifier of the next update will be chosen randomly instead of + sequentially. + message (:class:`telegram.Message`, optional): New incoming message of any kind - text, + photo, sticker, etc. + edited_message (:class:`telegram.Message`, optional): New version of a message that is + known to the bot and was edited. This update may at times be triggered by changes to + message fields that are either unavailable or not actively used by your bot. + channel_post (:class:`telegram.Message`, optional): New incoming channel post of any kind + - text, photo, sticker, etc. + edited_channel_post (:class:`telegram.Message`, optional): New version of a channel post + that is known to the bot and was edited. This update may at times be triggered by + changes to message fields that are either unavailable or not actively used by your bot. + inline_query (:class:`telegram.InlineQuery`, optional): New incoming inline query. + chosen_inline_result (:class:`telegram.ChosenInlineResult`, optional): The result of an + inline query that was chosen by a user and sent to their chat partner. + callback_query (:class:`telegram.CallbackQuery`, optional): New incoming callback query. + shipping_query (:class:`telegram.ShippingQuery`, optional): New incoming shipping query. + Only for invoices with flexible price. + pre_checkout_query (:class:`telegram.PreCheckoutQuery`, optional): New incoming + pre-checkout query. Contains full information about checkout. + poll (:class:`telegram.Poll`, optional): New poll state. Bots receive only updates about + manually stopped polls and polls, which are sent by the bot. + poll_answer (:class:`telegram.PollAnswer`, optional): A user changed their answer + in a non-anonymous poll. Bots receive new votes only in polls that were sent + by the bot itself. + my_chat_member (:class:`telegram.ChatMemberUpdated`, optional): The bot's chat member + status was updated in a chat. For private chats, this update is received only when the + bot is blocked or unblocked by the user. + + .. versionadded:: 13.4 + chat_member (:class:`telegram.ChatMemberUpdated`, optional): A chat member's status was + updated in a chat. The bot must be an administrator in the chat and must explicitly + specify :attr:`CHAT_MEMBER` in the list of + :paramref:`telegram.ext.Application.run_polling.allowed_updates` to receive these + updates (see :meth:`telegram.Bot.get_updates`, :meth:`telegram.Bot.set_webhook`, + :meth:`telegram.ext.Application.run_polling` and + :meth:`telegram.ext.Application.run_webhook`). + + .. versionadded:: 13.4 + chat_join_request (:class:`telegram.ChatJoinRequest`, optional): A request to join the + chat has been sent. The bot must have the + :attr:`telegram.ChatPermissions.can_invite_users` administrator right in the chat to + receive these updates. + + .. versionadded:: 13.8 + + chat_boost (:class:`telegram.ChatBoostUpdated`, optional): A chat boost was added or + changed. The bot must be an administrator in the chat to receive these updates. + + .. versionadded:: 20.8 + + removed_chat_boost (:class:`telegram.ChatBoostRemoved`, optional): A boost was removed from + a chat. The bot must be an administrator in the chat to receive these updates. + + .. versionadded:: 20.8 + + message_reaction (:class:`telegram.MessageReactionUpdated`, optional): A reaction to a + message was changed by a user. The bot must be an administrator in the chat and must + explicitly specify :attr:`MESSAGE_REACTION` in the list of + :paramref:`telegram.ext.Application.run_polling.allowed_updates` to receive these + updates (see :meth:`telegram.Bot.get_updates`, :meth:`telegram.Bot.set_webhook`, + :meth:`telegram.ext.Application.run_polling` and + :meth:`telegram.ext.Application.run_webhook`). The update isn't received for reactions + set by bots. + + .. versionadded:: 20.8 + + message_reaction_count (:class:`telegram.MessageReactionCountUpdated`, optional): Reactions + to a message with anonymous reactions were changed. The bot must be an administrator in + the chat and must explicitly specify :attr:`MESSAGE_REACTION_COUNT` in the list of + :paramref:`telegram.ext.Application.run_polling.allowed_updates` to receive these + updates (see :meth:`telegram.Bot.get_updates`, :meth:`telegram.Bot.set_webhook`, + :meth:`telegram.ext.Application.run_polling` and + :meth:`telegram.ext.Application.run_webhook`). The updates are grouped and can be sent + with delay up to a few minutes. + + .. versionadded:: 20.8 + + business_connection (:class:`telegram.BusinessConnection`, optional): The bot was connected + to or disconnected from a business account, or a user edited an existing connection + with the bot. + + .. versionadded:: 21.1 + + business_message (:class:`telegram.Message`, optional): New message from a connected + business account. + + .. versionadded:: 21.1 + + edited_business_message (:class:`telegram.Message`, optional): New version of a message + from a connected business account. + + .. versionadded:: 21.1 + + deleted_business_messages (:class:`telegram.BusinessMessagesDeleted`, optional): Messages + were deleted from a connected business account. + + .. versionadded:: 21.1 + + + Attributes: + update_id (:obj:`int`): The update's unique identifier. Update identifiers start from a + certain positive number and increase sequentially. This ID becomes especially handy if + you're using Webhooks, since it allows you to ignore repeated updates or to restore the + correct update sequence, should they get out of order. If there are no new updates for + at least a week, then identifier of the next update will be chosen randomly instead of + sequentially. + message (:class:`telegram.Message`): Optional. New incoming message of any kind - text, + photo, sticker, etc. + edited_message (:class:`telegram.Message`): Optional. New version of a message that is + known to the bot and was edited. This update may at times be triggered by changes to + message fields that are either unavailable or not actively used by your bot. + channel_post (:class:`telegram.Message`): Optional. New incoming channel post of any kind + - text, photo, sticker, etc. + edited_channel_post (:class:`telegram.Message`): Optional. New version of a channel post + that is known to the bot and was edited. This update may at times be triggered by + changes to message fields that are either unavailable or not actively used by your bot. + inline_query (:class:`telegram.InlineQuery`): Optional. New incoming inline query. + chosen_inline_result (:class:`telegram.ChosenInlineResult`): Optional. The result of an + inline query that was chosen by a user and sent to their chat partner. + callback_query (:class:`telegram.CallbackQuery`): Optional. New incoming callback query. + + Examples: + :any:`Arbitrary Callback Data Bot ` + shipping_query (:class:`telegram.ShippingQuery`): Optional. New incoming shipping query. + Only for invoices with flexible price. + pre_checkout_query (:class:`telegram.PreCheckoutQuery`): Optional. New incoming + pre-checkout query. Contains full information about checkout. + poll (:class:`telegram.Poll`): Optional. New poll state. Bots receive only updates about + manually stopped polls and polls, which are sent by the bot. + poll_answer (:class:`telegram.PollAnswer`): Optional. A user changed their answer + in a non-anonymous poll. Bots receive new votes only in polls that were sent + by the bot itself. + my_chat_member (:class:`telegram.ChatMemberUpdated`): Optional. The bot's chat member + status was updated in a chat. For private chats, this update is received only when the + bot is blocked or unblocked by the user. + + .. versionadded:: 13.4 + chat_member (:class:`telegram.ChatMemberUpdated`): Optional. A chat member's status was + updated in a chat. The bot must be an administrator in the chat and must explicitly + specify :attr:`CHAT_MEMBER` in the list of + :paramref:`telegram.ext.Application.run_polling.allowed_updates` to receive these + updates (see :meth:`telegram.Bot.get_updates`, :meth:`telegram.Bot.set_webhook`, + :meth:`telegram.ext.Application.run_polling` and + :meth:`telegram.ext.Application.run_webhook`). + + .. versionadded:: 13.4 + chat_join_request (:class:`telegram.ChatJoinRequest`): Optional. A request to join the + chat has been sent. The bot must have the + :attr:`telegram.ChatPermissions.can_invite_users` administrator right in the chat to + receive these updates. + + .. versionadded:: 13.8 + + chat_boost (:class:`telegram.ChatBoostUpdated`): Optional. A chat boost was added or + changed. The bot must be an administrator in the chat to receive these updates. + + .. versionadded:: 20.8 + + removed_chat_boost (:class:`telegram.ChatBoostRemoved`): Optional. A boost was removed from + a chat. The bot must be an administrator in the chat to receive these updates. + + .. versionadded:: 20.8 + + message_reaction (:class:`telegram.MessageReactionUpdated`): Optional. A reaction to a + message was changed by a user. The bot must be an administrator in the chat and must + explicitly specify :attr:`MESSAGE_REACTION` in the list of + :paramref:`telegram.ext.Application.run_polling.allowed_updates` to receive these + updates (see :meth:`telegram.Bot.get_updates`, :meth:`telegram.Bot.set_webhook`, + :meth:`telegram.ext.Application.run_polling` and + :meth:`telegram.ext.Application.run_webhook`). The update isn't received for reactions + set by bots. + + .. versionadded:: 20.8 + + message_reaction_count (:class:`telegram.MessageReactionCountUpdated`): Optional. Reactions + to a message with anonymous reactions were changed. The bot must be an administrator in + the chat and must explicitly specify :attr:`MESSAGE_REACTION_COUNT` in the list of + :paramref:`telegram.ext.Application.run_polling.allowed_updates` to receive these + updates (see :meth:`telegram.Bot.get_updates`, :meth:`telegram.Bot.set_webhook`, + :meth:`telegram.ext.Application.run_polling` and + :meth:`telegram.ext.Application.run_webhook`). The updates are grouped and can be sent + with delay up to a few minutes. + + .. versionadded:: 20.8 + + business_connection (:class:`telegram.BusinessConnection`): Optional. The bot was connected + to or disconnected from a business account, or a user edited an existing connection + with the bot. + + .. versionadded:: 21.1 + + business_message (:class:`telegram.Message`): Optional. New message from a connected + business account. + + .. versionadded:: 21.1 + + edited_business_message (:class:`telegram.Message`): Optional. New version of a message + from a connected business account. + + .. versionadded:: 21.1 + + deleted_business_messages (:class:`telegram.BusinessMessagesDeleted`): Optional. Messages + were deleted from a connected business account. + + .. versionadded:: 21.1 + """ + + __slots__ = ( + "_effective_chat", + "_effective_message", + "_effective_sender", + "_effective_user", + "business_connection", + "business_message", + "callback_query", + "channel_post", + "chat_boost", + "chat_join_request", + "chat_member", + "chosen_inline_result", + "deleted_business_messages", + "edited_business_message", + "edited_channel_post", + "edited_message", + "inline_query", + "message", + "message_reaction", + "message_reaction_count", + "my_chat_member", + "poll", + "poll_answer", + "pre_checkout_query", + "removed_chat_boost", + "shipping_query", + "update_id", + ) + + MESSAGE: Final[str] = constants.UpdateType.MESSAGE + """:const:`telegram.constants.UpdateType.MESSAGE` + + .. versionadded:: 13.5""" + EDITED_MESSAGE: Final[str] = constants.UpdateType.EDITED_MESSAGE + """:const:`telegram.constants.UpdateType.EDITED_MESSAGE` + + .. versionadded:: 13.5""" + CHANNEL_POST: Final[str] = constants.UpdateType.CHANNEL_POST + """:const:`telegram.constants.UpdateType.CHANNEL_POST` + + .. versionadded:: 13.5""" + EDITED_CHANNEL_POST: Final[str] = constants.UpdateType.EDITED_CHANNEL_POST + """:const:`telegram.constants.UpdateType.EDITED_CHANNEL_POST` + + .. versionadded:: 13.5""" + INLINE_QUERY: Final[str] = constants.UpdateType.INLINE_QUERY + """:const:`telegram.constants.UpdateType.INLINE_QUERY` + + .. versionadded:: 13.5""" + CHOSEN_INLINE_RESULT: Final[str] = constants.UpdateType.CHOSEN_INLINE_RESULT + """:const:`telegram.constants.UpdateType.CHOSEN_INLINE_RESULT` + + .. versionadded:: 13.5""" + CALLBACK_QUERY: Final[str] = constants.UpdateType.CALLBACK_QUERY + """:const:`telegram.constants.UpdateType.CALLBACK_QUERY` + + .. versionadded:: 13.5""" + SHIPPING_QUERY: Final[str] = constants.UpdateType.SHIPPING_QUERY + """:const:`telegram.constants.UpdateType.SHIPPING_QUERY` + + .. versionadded:: 13.5""" + PRE_CHECKOUT_QUERY: Final[str] = constants.UpdateType.PRE_CHECKOUT_QUERY + """:const:`telegram.constants.UpdateType.PRE_CHECKOUT_QUERY` + + .. versionadded:: 13.5""" + POLL: Final[str] = constants.UpdateType.POLL + """:const:`telegram.constants.UpdateType.POLL` + + .. versionadded:: 13.5""" + POLL_ANSWER: Final[str] = constants.UpdateType.POLL_ANSWER + """:const:`telegram.constants.UpdateType.POLL_ANSWER` + + .. versionadded:: 13.5""" + MY_CHAT_MEMBER: Final[str] = constants.UpdateType.MY_CHAT_MEMBER + """:const:`telegram.constants.UpdateType.MY_CHAT_MEMBER` + + .. versionadded:: 13.5""" + CHAT_MEMBER: Final[str] = constants.UpdateType.CHAT_MEMBER + """:const:`telegram.constants.UpdateType.CHAT_MEMBER` + + .. versionadded:: 13.5""" + CHAT_JOIN_REQUEST: Final[str] = constants.UpdateType.CHAT_JOIN_REQUEST + """:const:`telegram.constants.UpdateType.CHAT_JOIN_REQUEST` + + .. versionadded:: 13.8""" + CHAT_BOOST: Final[str] = constants.UpdateType.CHAT_BOOST + """:const:`telegram.constants.UpdateType.CHAT_BOOST` + + .. versionadded:: 20.8""" + REMOVED_CHAT_BOOST: Final[str] = constants.UpdateType.REMOVED_CHAT_BOOST + """:const:`telegram.constants.UpdateType.REMOVED_CHAT_BOOST` + + .. versionadded:: 20.8""" + MESSAGE_REACTION: Final[str] = constants.UpdateType.MESSAGE_REACTION + """:const:`telegram.constants.UpdateType.MESSAGE_REACTION` + + .. versionadded:: 20.8""" + MESSAGE_REACTION_COUNT: Final[str] = constants.UpdateType.MESSAGE_REACTION_COUNT + """:const:`telegram.constants.UpdateType.MESSAGE_REACTION_COUNT` + + .. versionadded:: 20.8""" + BUSINESS_CONNECTION: Final[str] = constants.UpdateType.BUSINESS_CONNECTION + """:const:`telegram.constants.UpdateType.BUSINESS_CONNECTION` + + .. versionadded:: 21.1""" + BUSINESS_MESSAGE: Final[str] = constants.UpdateType.BUSINESS_MESSAGE + """:const:`telegram.constants.UpdateType.BUSINESS_MESSAGE` + + .. versionadded:: 21.1""" + EDITED_BUSINESS_MESSAGE: Final[str] = constants.UpdateType.EDITED_BUSINESS_MESSAGE + """:const:`telegram.constants.UpdateType.EDITED_BUSINESS_MESSAGE` + + .. versionadded:: 21.1""" + DELETED_BUSINESS_MESSAGES: Final[str] = constants.UpdateType.DELETED_BUSINESS_MESSAGES + """:const:`telegram.constants.UpdateType.DELETED_BUSINESS_MESSAGES` + + .. versionadded:: 21.1""" + ALL_TYPES: Final[List[str]] = list(constants.UpdateType) + """List[:obj:`str`]: A list of all available update types. + + .. versionadded:: 13.5""" + + def __init__( + self, + update_id: int, + message: Optional[Message] = None, + edited_message: Optional[Message] = None, + channel_post: Optional[Message] = None, + edited_channel_post: Optional[Message] = None, + inline_query: Optional[InlineQuery] = None, + chosen_inline_result: Optional[ChosenInlineResult] = None, + callback_query: Optional[CallbackQuery] = None, + shipping_query: Optional[ShippingQuery] = None, + pre_checkout_query: Optional[PreCheckoutQuery] = None, + poll: Optional[Poll] = None, + poll_answer: Optional[PollAnswer] = None, + my_chat_member: Optional[ChatMemberUpdated] = None, + chat_member: Optional[ChatMemberUpdated] = None, + chat_join_request: Optional[ChatJoinRequest] = None, + chat_boost: Optional[ChatBoostUpdated] = None, + removed_chat_boost: Optional[ChatBoostRemoved] = None, + message_reaction: Optional[MessageReactionUpdated] = None, + message_reaction_count: Optional[MessageReactionCountUpdated] = None, + business_connection: Optional[BusinessConnection] = None, + business_message: Optional[Message] = None, + edited_business_message: Optional[Message] = None, + deleted_business_messages: Optional[BusinessMessagesDeleted] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.update_id: int = update_id + # Optionals + self.message: Optional[Message] = message + self.edited_message: Optional[Message] = edited_message + self.inline_query: Optional[InlineQuery] = inline_query + self.chosen_inline_result: Optional[ChosenInlineResult] = chosen_inline_result + self.callback_query: Optional[CallbackQuery] = callback_query + self.shipping_query: Optional[ShippingQuery] = shipping_query + self.pre_checkout_query: Optional[PreCheckoutQuery] = pre_checkout_query + self.channel_post: Optional[Message] = channel_post + self.edited_channel_post: Optional[Message] = edited_channel_post + self.poll: Optional[Poll] = poll + self.poll_answer: Optional[PollAnswer] = poll_answer + self.my_chat_member: Optional[ChatMemberUpdated] = my_chat_member + self.chat_member: Optional[ChatMemberUpdated] = chat_member + self.chat_join_request: Optional[ChatJoinRequest] = chat_join_request + self.chat_boost: Optional[ChatBoostUpdated] = chat_boost + self.removed_chat_boost: Optional[ChatBoostRemoved] = removed_chat_boost + self.message_reaction: Optional[MessageReactionUpdated] = message_reaction + self.message_reaction_count: Optional[MessageReactionCountUpdated] = message_reaction_count + self.business_connection: Optional[BusinessConnection] = business_connection + self.business_message: Optional[Message] = business_message + self.edited_business_message: Optional[Message] = edited_business_message + self.deleted_business_messages: Optional[BusinessMessagesDeleted] = ( + deleted_business_messages + ) + + self._effective_user: Optional[User] = None + self._effective_sender: Optional[Union[User, Chat]] = None + self._effective_chat: Optional[Chat] = None + self._effective_message: Optional[Message] = None + + self._id_attrs = (self.update_id,) + + self._freeze() + + @property + def effective_user(self) -> Optional["User"]: + """ + :class:`telegram.User`: The user that sent this update, no matter what kind of update this + is. If no user is associated with this update, this gives :obj:`None`. This is the case + if any of + + * :attr:`channel_post` + * :attr:`edited_channel_post` + * :attr:`poll` + * :attr:`chat_boost` + * :attr:`removed_chat_boost` + * :attr:`message_reaction_count` + * :attr:`deleted_business_messages` + + is present. + + .. versionchanged:: 21.1 + This property now also considers :attr:`business_connection`, :attr:`business_message` + and :attr:`edited_business_message`. + + Example: + * If :attr:`message` is present, this will give + :attr:`telegram.Message.from_user`. + * If :attr:`poll_answer` is present, this will give :attr:`telegram.PollAnswer.user`. + + """ + if self._effective_user: + return self._effective_user + + user = None + + if self.message: + user = self.message.from_user + + elif self.edited_message: + user = self.edited_message.from_user + + elif self.inline_query: + user = self.inline_query.from_user + + elif self.chosen_inline_result: + user = self.chosen_inline_result.from_user + + elif self.callback_query: + user = self.callback_query.from_user + + elif self.shipping_query: + user = self.shipping_query.from_user + + elif self.pre_checkout_query: + user = self.pre_checkout_query.from_user + + elif self.poll_answer: + user = self.poll_answer.user + + elif self.my_chat_member: + user = self.my_chat_member.from_user + + elif self.chat_member: + user = self.chat_member.from_user + + elif self.chat_join_request: + user = self.chat_join_request.from_user + + elif self.message_reaction: + user = self.message_reaction.user + + elif self.business_message: + user = self.business_message.from_user + + elif self.edited_business_message: + user = self.edited_business_message.from_user + + elif self.business_connection: + user = self.business_connection.user + + self._effective_user = user + return user + + @property + def effective_sender(self) -> Optional[Union["User", "Chat"]]: + """ + :class:`telegram.User` or :class:`telegram.Chat`: The user or chat that sent this update, + no matter what kind of update this is. + + Note: + * Depending on the type of update and the user's 'Remain anonymous' setting, this + could either be :class:`telegram.User`, :class:`telegram.Chat` or :obj:`None`. + + If no user whatsoever is associated with this update, this gives :obj:`None`. This + is the case if any of + + * :attr:`poll` + * :attr:`chat_boost` + * :attr:`removed_chat_boost` + * :attr:`message_reaction_count` + * :attr:`deleted_business_messages` + + is present. + + Example: + * If :attr:`message` is present, this will give either + :attr:`telegram.Message.from_user` or :attr:`telegram.Message.sender_chat`. + * If :attr:`poll_answer` is present, this will give either + :attr:`telegram.PollAnswer.user` or :attr:`telegram.PollAnswer.voter_chat`. + * If :attr:`channel_post` is present, this will give + :attr:`telegram.Message.sender_chat`. + + .. versionadded:: 21.1 + """ + if self._effective_sender: + return self._effective_sender + + sender: Optional[Union[User, Chat]] = None + + if message := ( + self.message + or self.edited_message + or self.channel_post + or self.edited_channel_post + or self.business_message + or self.edited_business_message + ): + sender = message.sender_chat + + elif self.poll_answer: + sender = self.poll_answer.voter_chat + + elif self.message_reaction: + sender = self.message_reaction.actor_chat + + if sender is None: + sender = self.effective_user + + self._effective_sender = sender + return sender + + @property + def effective_chat(self) -> Optional["Chat"]: + """ + :class:`telegram.Chat`: The chat that this update was sent in, no matter what kind of + update this is. + If no chat is associated with this update, this gives :obj:`None`. + This is the case, if :attr:`inline_query`, + :attr:`chosen_inline_result`, :attr:`callback_query` from inline messages, + :attr:`shipping_query`, :attr:`pre_checkout_query`, :attr:`poll`, + :attr:`poll_answer`, or :attr:`business_connection` is present. + + .. versionchanged:: 21.1 + This property now also considers :attr:`business_message`, + :attr:`edited_business_message`, and :attr:`deleted_business_messages`. + + Example: + If :attr:`message` is present, this will give :attr:`telegram.Message.chat`. + + """ + if self._effective_chat: + return self._effective_chat + + chat = None + + if self.message: + chat = self.message.chat + + elif self.edited_message: + chat = self.edited_message.chat + + elif self.callback_query and self.callback_query.message: + chat = self.callback_query.message.chat + + elif self.channel_post: + chat = self.channel_post.chat + + elif self.edited_channel_post: + chat = self.edited_channel_post.chat + + elif self.my_chat_member: + chat = self.my_chat_member.chat + + elif self.chat_member: + chat = self.chat_member.chat + + elif self.chat_join_request: + chat = self.chat_join_request.chat + + elif self.chat_boost: + chat = self.chat_boost.chat + + elif self.removed_chat_boost: + chat = self.removed_chat_boost.chat + + elif self.message_reaction: + chat = self.message_reaction.chat + + elif self.message_reaction_count: + chat = self.message_reaction_count.chat + + elif self.business_message: + chat = self.business_message.chat + + elif self.edited_business_message: + chat = self.edited_business_message.chat + + elif self.deleted_business_messages: + chat = self.deleted_business_messages.chat + + self._effective_chat = chat + return chat + + @property + def effective_message(self) -> Optional[Message]: + """ + :class:`telegram.Message`: The message included in this update, no matter what kind of + update this is. More precisely, this will be the message contained in :attr:`message`, + :attr:`edited_message`, :attr:`channel_post`, :attr:`edited_channel_post` or + :attr:`callback_query` (i.e. :attr:`telegram.CallbackQuery.message`) or :obj:`None`, if + none of those are present. + + .. versionchanged:: 21.1 + This property now also considers :attr:`business_message`, and + :attr:`edited_business_message`. + + Tip: + This property will only ever return objects of type :class:`telegram.Message` or + :obj:`None`, never :class:`telegram.MaybeInaccessibleMessage` or + :class:`telegram.InaccessibleMessage`. + Currently, this is only relevant for :attr:`callback_query`, as + :attr:`telegram.CallbackQuery.message` is the only attribute considered by this + property that can be an object of these types. + """ + if self._effective_message: + return self._effective_message + + message: Optional[Message] = None + + if self.message: + message = self.message + + elif self.edited_message: + message = self.edited_message + + elif self.callback_query: + if ( + isinstance(cbq_message := self.callback_query.message, Message) + or cbq_message is None + ): + message = cbq_message + else: + warn( + ( + "`update.callback_query` is not `None`, but of type " + f"`{cbq_message.__class__.__name__}`. This is not considered by " + "`Update.effective_message`. Please manually access this attribute " + "if necessary." + ), + stacklevel=2, + ) + + elif self.channel_post: + message = self.channel_post + + elif self.edited_channel_post: + message = self.edited_channel_post + + elif self.business_message: + message = self.business_message + + elif self.edited_business_message: + message = self.edited_business_message + + self._effective_message = message + return message + + @classmethod + def de_json(cls, data: Optional[JSONDict], bot: Optional["Bot"] = None) -> Optional["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["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["message_reaction_count"] = MessageReactionCountUpdated.de_json( + data.get("message_reaction_count"), bot + ) + data["business_connection"] = BusinessConnection.de_json( + data.get("business_connection"), 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 + ) + + return super().de_json(data=data, bot=bot) diff --git a/_user.py b/_user.py new file mode 100644 index 0000000000000000000000000000000000000000..075c4f12861d35aca01b728f481d5bc09afffe67 --- /dev/null +++ b/_user.py @@ -0,0 +1,2195 @@ +#!/usr/bin/env python +# pylint: disable=redefined-builtin +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram User.""" +from datetime import datetime +from typing import TYPE_CHECKING, Optional, Sequence, Tuple, Union + +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.helpers import mention_html as helpers_mention_html +from telegram.helpers import mention_markdown as helpers_mention_markdown + +if TYPE_CHECKING: + from telegram import ( + Animation, + Audio, + Contact, + Document, + InlineKeyboardMarkup, + InputMediaAudio, + InputMediaDocument, + InputMediaPhoto, + InputMediaVideo, + InputPollOption, + LabeledPrice, + LinkPreviewOptions, + Location, + Message, + MessageEntity, + MessageId, + PhotoSize, + ReplyParameters, + Sticker, + UserChatBoosts, + UserProfilePhotos, + Venue, + Video, + VideoNote, + Voice, + ) + + +class User(TelegramObject): + """This object represents a Telegram user or bot. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`id` is equal. + + .. versionchanged:: 20.0 + The following are now keyword-only arguments in Bot methods: + ``location``, ``filename``, ``venue``, ``contact``, + ``{read, write, connect, pool}_timeout`` ``api_kwargs``. Use a named argument for those, + and notice that some positional arguments changed position as a result. + + Args: + id (:obj:`int`): Unique identifier for this user or bot. + is_bot (:obj:`bool`): :obj:`True`, if this user is a bot. + first_name (:obj:`str`): User's or bot's first name. + last_name (:obj:`str`, optional): User's or bot's last name. + username (:obj:`str`, optional): User's or bot's username. + language_code (:obj:`str`, optional): IETF language tag of the user's language. + can_join_groups (:obj:`str`, optional): :obj:`True`, if the bot can be invited to groups. + Returned only in :meth:`telegram.Bot.get_me`. + can_read_all_group_messages (:obj:`str`, optional): :obj:`True`, if privacy mode is + disabled for the bot. Returned only in :meth:`telegram.Bot.get_me`. + supports_inline_queries (:obj:`str`, optional): :obj:`True`, if the bot supports inline + queries. Returned only in :meth:`telegram.Bot.get_me`. + + is_premium (:obj:`bool`, optional): :obj:`True`, if this user is a Telegram Premium user. + + .. versionadded:: 20.0 + added_to_attachment_menu (:obj:`bool`, optional): :obj:`True`, if this user added + the bot to the attachment menu. + + .. versionadded:: 20.0 + can_connect_to_business (:obj:`bool`, optional): :obj:`True`, if the bot can be connected + to a Telegram Business account to receive its messages. Returned only in + :meth:`telegram.Bot.get_me`. + + .. versionadded:: 21.1 + has_main_web_app (:obj:`bool`, optional): :obj:`True`, if the bot has the main Web App. + Returned only in :meth:`telegram.Bot.get_me`. + + .. versionadded:: 21.5 + + Attributes: + id (:obj:`int`): Unique identifier for this user or bot. + is_bot (:obj:`bool`): :obj:`True`, if this user is a bot. + first_name (:obj:`str`): User's or bot's first name. + last_name (:obj:`str`): Optional. User's or bot's last name. + username (:obj:`str`): Optional. User's or bot's username. + language_code (:obj:`str`): Optional. IETF language tag of the user's language. + can_join_groups (:obj:`str`): Optional. :obj:`True`, if the bot can be invited to groups. + Returned only in :attr:`telegram.Bot.get_me` requests. + can_read_all_group_messages (:obj:`str`): Optional. :obj:`True`, if privacy mode is + disabled for the bot. Returned only in :attr:`telegram.Bot.get_me` requests. + supports_inline_queries (:obj:`str`): Optional. :obj:`True`, if the bot supports inline + queries. Returned only in :attr:`telegram.Bot.get_me` requests. + is_premium (:obj:`bool`): Optional. :obj:`True`, if this user is a Telegram + Premium user. + + .. versionadded:: 20.0 + added_to_attachment_menu (:obj:`bool`): Optional. :obj:`True`, if this user added + the bot to the attachment menu. + + .. versionadded:: 20.0 + can_connect_to_business (:obj:`bool`): Optional. :obj:`True`, if the bot can be connected + to a Telegram Business account to receive its messages. Returned only in + :meth:`telegram.Bot.get_me`. + + .. versionadded:: 21.1 + has_main_web_app (:obj:`bool`) Optional. :obj:`True`, if the bot has the main Web App. + Returned only in :meth:`telegram.Bot.get_me`. + + .. versionadded:: 21.5 + + .. |user_chat_id_note| replace:: This shortcuts build on the assumption that :attr:`User.id` + coincides with the :attr:`Chat.id` of the private chat with the user. This has been the + case so far, but Telegram does not guarantee that this stays this way. + """ + + __slots__ = ( + "added_to_attachment_menu", + "can_connect_to_business", + "can_join_groups", + "can_read_all_group_messages", + "first_name", + "has_main_web_app", + "id", + "is_bot", + "is_premium", + "language_code", + "last_name", + "supports_inline_queries", + "username", + ) + + def __init__( + self, + id: int, + first_name: str, + is_bot: bool, + last_name: Optional[str] = None, + username: Optional[str] = None, + language_code: Optional[str] = None, + can_join_groups: Optional[bool] = None, + can_read_all_group_messages: Optional[bool] = None, + supports_inline_queries: Optional[bool] = None, + is_premium: Optional[bool] = None, + added_to_attachment_menu: Optional[bool] = None, + can_connect_to_business: Optional[bool] = None, + has_main_web_app: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.id: int = id + self.first_name: str = first_name + self.is_bot: bool = is_bot + # Optionals + self.last_name: Optional[str] = last_name + self.username: Optional[str] = username + self.language_code: Optional[str] = language_code + self.can_join_groups: Optional[bool] = can_join_groups + self.can_read_all_group_messages: Optional[bool] = can_read_all_group_messages + self.supports_inline_queries: Optional[bool] = supports_inline_queries + self.is_premium: Optional[bool] = is_premium + self.added_to_attachment_menu: Optional[bool] = added_to_attachment_menu + self.can_connect_to_business: Optional[bool] = can_connect_to_business + self.has_main_web_app: Optional[bool] = has_main_web_app + + self._id_attrs = (self.id,) + + self._freeze() + + @property + def name(self) -> str: + """:obj:`str`: Convenience property. If available, returns the user's :attr:`username` + prefixed with "@". If :attr:`username` is not available, returns :attr:`full_name`. + """ + if self.username: + return f"@{self.username}" + return self.full_name + + @property + def full_name(self) -> str: + """:obj:`str`: Convenience property. The user's :attr:`first_name`, followed by (if + available) :attr:`last_name`. + """ + if self.last_name: + return f"{self.first_name} {self.last_name}" + return self.first_name + + @property + def link(self) -> Optional[str]: + """:obj:`str`: Convenience property. If :attr:`username` is available, returns a t.me link + of the user. + """ + if self.username: + return f"https://t.me/{self.username}" + return None + + async def get_profile_photos( + self, + offset: Optional[int] = None, + limit: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Optional["UserProfilePhotos"]: + """Shortcut for:: + + await bot.get_user_profile_photos(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.get_user_profile_photos`. + + """ + return await self.get_bot().get_user_profile_photos( + user_id=self.id, + offset=offset, + limit=limit, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + def mention_markdown(self, name: Optional[str] = None) -> str: + """ + Note: + :tg-const:`telegram.constants.ParseMode.MARKDOWN` is a legacy mode, retained by + Telegram for backward compatibility. You should use :meth:`mention_markdown_v2` + instead. + + Args: + name (:obj:`str`): The name used as a link for the user. Defaults to :attr:`full_name`. + + Returns: + :obj:`str`: The inline mention for the user as markdown (version 1). + + """ + if name: + return helpers_mention_markdown(self.id, name) + return helpers_mention_markdown(self.id, self.full_name) + + def mention_markdown_v2(self, name: Optional[str] = None) -> str: + """ + Args: + name (:obj:`str`): The name used as a link for the user. Defaults to :attr:`full_name`. + + Returns: + :obj:`str`: The inline mention for the user as markdown (version 2). + + """ + if name: + return helpers_mention_markdown(self.id, name, version=2) + return helpers_mention_markdown(self.id, self.full_name, version=2) + + def mention_html(self, name: Optional[str] = None) -> str: + """ + Args: + name (:obj:`str`): The name used as a link for the user. Defaults to :attr:`full_name`. + + Returns: + :obj:`str`: The inline mention for the user as HTML. + + """ + if name: + return helpers_mention_html(self.id, name) + return helpers_mention_html(self.id, self.full_name) + + def mention_button(self, name: Optional[str] = None) -> InlineKeyboardButton: + """Shortcut for:: + + InlineKeyboardButton(text=name, url=f"tg://user?id={update.effective_user.id}") + + .. versionadded:: 13.9 + + Args: + name (:obj:`str`): The name used as a link for the user. Defaults to :attr:`full_name`. + + Returns: + :class:`telegram.InlineKeyboardButton`: InlineButton with url set to the user mention + """ + return InlineKeyboardButton(text=name or self.full_name, url=f"tg://user?id={self.id}") + + async def pin_message( + self, + message_id: int, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + business_connection_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.pin_chat_message(chat_id=update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.pin_chat_message`. + + Note: + |user_chat_id_note| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().pin_chat_message( + chat_id=self.id, + message_id=message_id, + disable_notification=disable_notification, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + business_connection_id=business_connection_id, + api_kwargs=api_kwargs, + ) + + async def unpin_message( + self, + message_id: Optional[int] = None, + business_connection_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.unpin_chat_message(chat_id=update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.unpin_chat_message`. + + Note: + |user_chat_id_note| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().unpin_chat_message( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + message_id=message_id, + business_connection_id=business_connection_id, + ) + + async def unpin_all_messages( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.unpin_all_chat_messages(chat_id=update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.unpin_all_chat_messages`. + + Note: + |user_chat_id_note| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().unpin_all_chat_messages( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def send_message( + self, + text: str, + parse_mode: ODVInput[str] = DEFAULT_NONE, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + disable_web_page_preview: Optional[bool] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_message(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_message`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_message( + chat_id=self.id, + text=text, + parse_mode=parse_mode, + disable_web_page_preview=disable_web_page_preview, + link_preview_options=link_preview_options, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + allow_sending_without_reply=allow_sending_without_reply, + entities=entities, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def delete_message( + self, + message_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.delete_message(update.effective_user.id, *argss, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.delete_message`. + + .. versionadded:: 20.8 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().delete_message( + chat_id=self.id, + message_id=message_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_messages( + self, + message_ids: Sequence[int], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.delete_messages(update.effective_user.id, *argss, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.delete_messages`. + + .. versionadded:: 20.8 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().delete_messages( + chat_id=self.id, + message_ids=message_ids, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def send_photo( + self, + photo: Union[FileInput, "PhotoSize"], + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + has_spoiler: Optional[bool] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_photo(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_photo`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_photo( + chat_id=self.id, + photo=photo, + caption=caption, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + parse_mode=parse_mode, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + has_spoiler=has_spoiler, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, + ) + + async def send_media_group( + self, + media: Sequence[ + Union["InputMediaAudio", "InputMediaDocument", "InputMediaPhoto", "InputMediaVideo"] + ], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + ) -> Tuple["Message", ...]: + """Shortcut for:: + + await bot.send_media_group(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_media_group`. + + Note: + |user_chat_id_note| + + Returns: + Tuple[:class:`telegram.Message`:] On success, a tuple of :class:`~telegram.Message` + instances that were sent is returned. + + """ + return await self.get_bot().send_media_group( + chat_id=self.id, + media=media, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_audio( + self, + audio: Union[FileInput, "Audio"], + duration: Optional[int] = None, + performer: Optional[str] = None, + title: Optional[str] = None, + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_audio(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_audio`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_audio( + chat_id=self.id, + audio=audio, + duration=duration, + performer=performer, + title=title, + caption=caption, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + parse_mode=parse_mode, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + thumbnail=thumbnail, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_chat_action( + self, + action: str, + message_thread_id: Optional[int] = None, + business_connection_id: Optional[str] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.send_chat_action(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_chat_action`. + + Note: + |user_chat_id_note| + + Returns: + :obj:`True`: On success. + + """ + return await self.get_bot().send_chat_action( + chat_id=self.id, + action=action, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + business_connection_id=business_connection_id, + ) + + send_action = send_chat_action + """Alias for :attr:`send_chat_action`""" + + async def send_contact( + self, + phone_number: Optional[str] = None, + first_name: Optional[str] = None, + last_name: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + vcard: Optional[str] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + contact: Optional["Contact"] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_contact(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_contact`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_contact( + chat_id=self.id, + phone_number=phone_number, + first_name=first_name, + last_name=last_name, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + contact=contact, + vcard=vcard, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_dice( + self, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + emoji: Optional[str] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_dice(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_dice`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_dice( + chat_id=self.id, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + emoji=emoji, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_document( + self, + document: Union[FileInput, "Document"], + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + disable_content_type_detection: Optional[bool] = None, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_document(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_document`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_document( + chat_id=self.id, + document=document, + filename=filename, + caption=caption, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + parse_mode=parse_mode, + thumbnail=thumbnail, + api_kwargs=api_kwargs, + disable_content_type_detection=disable_content_type_detection, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_game( + self, + game_short_name: str, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_game(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_game`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_game( + chat_id=self.id, + game_short_name=game_short_name, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_invoice( + self, + title: str, + description: str, + payload: str, + provider_token: Optional[str], + currency: str, + prices: Sequence["LabeledPrice"], + start_parameter: Optional[str] = None, + photo_url: Optional[str] = None, + photo_size: Optional[int] = None, + photo_width: Optional[int] = None, + photo_height: Optional[int] = None, + need_name: Optional[bool] = None, + need_phone_number: Optional[bool] = None, + need_email: Optional[bool] = None, + need_shipping_address: Optional[bool] = None, + is_flexible: Optional[bool] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional["InlineKeyboardMarkup"] = None, + provider_data: Optional[Union[str, object]] = None, + send_phone_number_to_provider: Optional[bool] = None, + send_email_to_provider: Optional[bool] = None, + max_tip_amount: Optional[int] = None, + suggested_tip_amounts: Optional[Sequence[int]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_invoice(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_invoice`. + + Warning: + As of API 5.2 :paramref:`start_parameter ` + is an optional argument and therefore the + order of the arguments had to be changed. Use keyword arguments to make sure that the + arguments are passed correctly. + + Note: + |user_chat_id_note| + + .. versionchanged:: 13.5 + As of Bot API 5.2, the parameter + :paramref:`start_parameter ` is optional. + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_invoice( + chat_id=self.id, + title=title, + description=description, + payload=payload, + provider_token=provider_token, + currency=currency, + prices=prices, + start_parameter=start_parameter, + photo_url=photo_url, + photo_size=photo_size, + photo_width=photo_width, + photo_height=photo_height, + need_name=need_name, + need_phone_number=need_phone_number, + need_email=need_email, + need_shipping_address=need_shipping_address, + is_flexible=is_flexible, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + provider_data=provider_data, + send_phone_number_to_provider=send_phone_number_to_provider, + send_email_to_provider=send_email_to_provider, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + max_tip_amount=max_tip_amount, + suggested_tip_amounts=suggested_tip_amounts, + protect_content=protect_content, + message_thread_id=message_thread_id, + message_effect_id=message_effect_id, + ) + + async def send_location( + self, + latitude: Optional[float] = None, + longitude: Optional[float] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + live_period: Optional[int] = None, + horizontal_accuracy: Optional[float] = None, + heading: Optional[int] = None, + proximity_alert_radius: Optional[int] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + location: Optional["Location"] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_location(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_location`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_location( + chat_id=self.id, + latitude=latitude, + longitude=longitude, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + location=location, + live_period=live_period, + api_kwargs=api_kwargs, + horizontal_accuracy=horizontal_accuracy, + heading=heading, + proximity_alert_radius=proximity_alert_radius, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_animation( + self, + animation: Union[FileInput, "Animation"], + duration: Optional[int] = None, + width: Optional[int] = None, + height: Optional[int] = None, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + has_spoiler: Optional[bool] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_animation(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_animation`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_animation( + chat_id=self.id, + animation=animation, + duration=duration, + width=width, + height=height, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + has_spoiler=has_spoiler, + thumbnail=thumbnail, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, + ) + + async def send_sticker( + self, + sticker: Union[FileInput, "Sticker"], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + emoji: Optional[str] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_sticker(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_sticker`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_sticker( + chat_id=self.id, + sticker=sticker, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + emoji=emoji, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_video( + self, + video: Union[FileInput, "Video"], + duration: Optional[int] = None, + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + width: Optional[int] = None, + height: Optional[int] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + supports_streaming: Optional[bool] = None, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + has_spoiler: Optional[bool] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_video(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_video`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_video( + chat_id=self.id, + video=video, + duration=duration, + caption=caption, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + width=width, + height=height, + parse_mode=parse_mode, + supports_streaming=supports_streaming, + thumbnail=thumbnail, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + has_spoiler=has_spoiler, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, + ) + + async def send_venue( + self, + latitude: Optional[float] = None, + longitude: Optional[float] = None, + title: Optional[str] = None, + address: Optional[str] = None, + foursquare_id: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + foursquare_type: Optional[str] = None, + google_place_id: Optional[str] = None, + google_place_type: Optional[str] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + venue: Optional["Venue"] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_venue(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_venue`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_venue( + chat_id=self.id, + latitude=latitude, + longitude=longitude, + title=title, + address=address, + foursquare_id=foursquare_id, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + venue=venue, + foursquare_type=foursquare_type, + api_kwargs=api_kwargs, + google_place_id=google_place_id, + google_place_type=google_place_type, + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_video_note( + self, + video_note: Union[FileInput, "VideoNote"], + duration: Optional[int] = None, + length: Optional[int] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + thumbnail: Optional[FileInput] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_video_note(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_video_note`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_video_note( + chat_id=self.id, + video_note=video_note, + duration=duration, + length=length, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + thumbnail=thumbnail, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_voice( + self, + voice: Union[FileInput, "Voice"], + duration: Optional[int] = None, + caption: Optional[str] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + filename: Optional[str] = None, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_voice(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_voice`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_voice( + chat_id=self.id, + voice=voice, + duration=duration, + caption=caption, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + parse_mode=parse_mode, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + caption_entities=caption_entities, + filename=filename, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + ) + + async def send_poll( + self, + question: str, + options: Sequence[Union[str, "InputPollOption"]], + is_anonymous: Optional[bool] = None, + type: Optional[str] = None, + allows_multiple_answers: Optional[bool] = None, + correct_option_id: Optional[CorrectOptionID] = None, + is_closed: Optional[bool] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + explanation: Optional[str] = None, + explanation_parse_mode: ODVInput[str] = DEFAULT_NONE, + open_period: Optional[int] = None, + close_date: Optional[Union[int, datetime]] = None, + explanation_entities: Optional[Sequence["MessageEntity"]] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + business_connection_id: Optional[str] = None, + question_parse_mode: ODVInput[str] = DEFAULT_NONE, + question_entities: Optional[Sequence["MessageEntity"]] = None, + message_effect_id: Optional[str] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.send_poll(update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.send_poll`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().send_poll( + chat_id=self.id, + question=question, + options=options, + is_anonymous=is_anonymous, + type=type, # pylint=pylint, + allows_multiple_answers=allows_multiple_answers, + correct_option_id=correct_option_id, + is_closed=is_closed, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + explanation=explanation, + explanation_parse_mode=explanation_parse_mode, + open_period=open_period, + close_date=close_date, + api_kwargs=api_kwargs, + allow_sending_without_reply=allow_sending_without_reply, + explanation_entities=explanation_entities, + protect_content=protect_content, + message_thread_id=message_thread_id, + business_connection_id=business_connection_id, + question_parse_mode=question_parse_mode, + question_entities=question_entities, + message_effect_id=message_effect_id, + ) + + async def send_copy( + self, + from_chat_id: Union[str, int], + message_id: int, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "MessageId": + """Shortcut for:: + + await bot.copy_message(chat_id=update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.copy_message`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().copy_message( + chat_id=self.id, + from_chat_id=from_chat_id, + message_id=message_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + allow_sending_without_reply=allow_sending_without_reply, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + protect_content=protect_content, + message_thread_id=message_thread_id, + show_caption_above_media=show_caption_above_media, + ) + + async def copy_message( + self, + chat_id: Union[int, str], + message_id: int, + caption: Optional[str] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence["MessageEntity"]] = None, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + reply_markup: Optional[ReplyMarkup] = None, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, + *, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "MessageId": + """Shortcut for:: + + await bot.copy_message(from_chat_id=update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.copy_message`. + + Note: + |user_chat_id_note| + + Returns: + :class:`telegram.MessageId`: On success, returns the MessageId of the sent message. + + """ + return await self.get_bot().copy_message( + from_chat_id=self.id, + chat_id=chat_id, + message_id=message_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_parameters=reply_parameters, + allow_sending_without_reply=allow_sending_without_reply, + reply_markup=reply_markup, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + protect_content=protect_content, + message_thread_id=message_thread_id, + show_caption_above_media=show_caption_above_media, + ) + + async def send_copies( + self, + from_chat_id: Union[str, int], + message_ids: Sequence[int], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + remove_caption: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple["MessageId", ...]: + """Shortcut for:: + + await bot.copy_messages(chat_id=update.effective_user.id, *argss, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.copy_messages`. + + .. seealso:: :meth:`copy_message`, :meth:`send_copy`, :meth:`copy_messages`. + + .. versionadded:: 20.8 + + Returns: + Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + of the sent messages is returned. + + """ + return await self.get_bot().copy_messages( + chat_id=self.id, + from_chat_id=from_chat_id, + message_ids=message_ids, + disable_notification=disable_notification, + protect_content=protect_content, + message_thread_id=message_thread_id, + remove_caption=remove_caption, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def copy_messages( + self, + chat_id: Union[str, int], + message_ids: Sequence[int], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + remove_caption: Optional[bool] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple["MessageId", ...]: + """Shortcut for:: + + await bot.copy_messages(from_chat_id=update.effective_user.id, *argss, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.copy_messages`. + + .. seealso:: :meth:`copy_message`, :meth:`send_copy`, :meth:`send_copies`. + + .. versionadded:: 20.8 + + Returns: + Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + of the sent messages is returned. + + """ + return await self.get_bot().copy_messages( + from_chat_id=self.id, + chat_id=chat_id, + message_ids=message_ids, + disable_notification=disable_notification, + protect_content=protect_content, + message_thread_id=message_thread_id, + remove_caption=remove_caption, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def forward_from( + self, + from_chat_id: Union[str, int], + message_id: int, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.forward_message(chat_id=update.effective_user.id, *argss, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.forward_message`. + + .. seealso:: :meth:`forward_to`, :meth:`forward_messages_from`, :meth:`forward_messages_to` + + .. versionadded:: 20.0 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().forward_message( + chat_id=self.id, + from_chat_id=from_chat_id, + message_id=message_id, + disable_notification=disable_notification, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + protect_content=protect_content, + message_thread_id=message_thread_id, + ) + + async def forward_to( + self, + chat_id: Union[int, str], + message_id: int, + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "Message": + """Shortcut for:: + + await bot.forward_message(from_chat_id=update.effective_user.id, *argss, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.forward_message`. + + .. seealso:: :meth:`forward_from`, :meth:`forward_messages_from`, + :meth:`forward_messages_to` + + .. versionadded:: 20.0 + + Returns: + :class:`telegram.Message`: On success, instance representing the message posted. + + """ + return await self.get_bot().forward_message( + from_chat_id=self.id, + chat_id=chat_id, + message_id=message_id, + disable_notification=disable_notification, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + protect_content=protect_content, + message_thread_id=message_thread_id, + ) + + async def forward_messages_from( + self, + from_chat_id: Union[str, int], + message_ids: Sequence[int], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple["MessageId", ...]: + """Shortcut for:: + + await bot.forward_messages(chat_id=update.effective_user.id, *argss, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.forward_messages`. + + .. seealso:: :meth:`forward_to`, :meth:`forward_from`, :meth:`forward_messages_to`. + + .. versionadded:: 20.8 + + Returns: + Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + of sent messages is returned. + + """ + return await self.get_bot().forward_messages( + chat_id=self.id, + from_chat_id=from_chat_id, + message_ids=message_ids, + disable_notification=disable_notification, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def forward_messages_to( + self, + chat_id: Union[int, str], + message_ids: Sequence[int], + disable_notification: ODVInput[bool] = DEFAULT_NONE, + protect_content: ODVInput[bool] = DEFAULT_NONE, + message_thread_id: Optional[int] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> Tuple["MessageId", ...]: + """Shortcut for:: + + await bot.forward_messages(from_chat_id=update.effective_user.id, *argss, **kwargs) + + For the documentation of the arguments, please see :meth:`telegram.Bot.forward_messages`. + + .. seealso:: :meth:`forward_from`, :meth:`forward_to`, :meth:`forward_messages_from`. + + .. versionadded:: 20.8 + + Returns: + Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + of sent messages is returned. + + """ + return await self.get_bot().forward_messages( + from_chat_id=self.id, + chat_id=chat_id, + message_ids=message_ids, + disable_notification=disable_notification, + protect_content=protect_content, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def approve_join_request( + self, + chat_id: Union[int, str], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.approve_chat_join_request(user_id=update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.approve_chat_join_request`. + + Note: + |user_chat_id_note| + + .. versionadded:: 13.8 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().approve_chat_join_request( + user_id=self.id, + chat_id=chat_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def decline_join_request( + self, + chat_id: Union[int, str], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.decline_chat_join_request(user_id=update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.decline_chat_join_request`. + + Note: + |user_chat_id_note| + + .. versionadded:: 13.8 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + """ + return await self.get_bot().decline_chat_join_request( + user_id=self.id, + chat_id=chat_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def set_menu_button( + self, + menu_button: Optional[MenuButton] = None, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.set_chat_menu_button(chat_id=update.effective_user.id, *argss, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.set_chat_menu_button`. + + .. seealso:: :meth:`get_menu_button` + + Note: + |user_chat_id_note| + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().set_chat_menu_button( + chat_id=self.id, + menu_button=menu_button, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_menu_button( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> MenuButton: + """Shortcut for:: + + await bot.get_chat_menu_button(chat_id=update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.get_chat_menu_button`. + + .. seealso:: :meth:`set_menu_button` + + Note: + |user_chat_id_note| + + .. versionadded:: 20.0 + + Returns: + :class:`telegram.MenuButton`: On success, the current menu button is returned. + """ + return await self.get_bot().get_chat_menu_button( + chat_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def get_chat_boosts( + self, + chat_id: Union[int, str], + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> "UserChatBoosts": + """Shortcut for:: + + await bot.get_user_chat_boosts(user_id=update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.get_user_chat_boosts`. + + .. versionadded:: 20.8 + + Returns: + :class:`telegram.UserChatBoosts`: On success, returns the boosts applied by the user. + """ + return await self.get_bot().get_user_chat_boosts( + chat_id=chat_id, + user_id=self.id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def refund_star_payment( + self, + telegram_payment_charge_id: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.refund_star_payment(user_id=update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.refund_star_payment`. + + .. versionadded:: 21.3 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().refund_star_payment( + user_id=self.id, + telegram_payment_charge_id=telegram_payment_charge_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) diff --git a/_userprofilephotos.py b/_userprofilephotos.py new file mode 100644 index 0000000000000000000000000000000000000000..9a5e4a066efc68190d870b4b23a44cb7a5bb0b0f --- /dev/null +++ b/_userprofilephotos.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram UserProfilePhotos.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._files.photosize import PhotoSize +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class UserProfilePhotos(TelegramObject): + """This object represents a user's profile pictures. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`total_count` and :attr:`photos` are equal. + + Args: + total_count (:obj:`int`): Total number of profile pictures the target user has. + photos (Sequence[Sequence[:class:`telegram.PhotoSize`]]): Requested profile pictures (in up + to 4 sizes each). + + .. versionchanged:: 20.0 + |sequenceclassargs| + + Attributes: + total_count (:obj:`int`): Total number of profile pictures. + photos (Tuple[Tuple[:class:`telegram.PhotoSize`]]): Requested profile pictures (in up to 4 + sizes each). + + .. versionchanged:: 20.0 + |tupleclassattrs| + + """ + + __slots__ = ("photos", "total_count") + + def __init__( + self, + total_count: int, + photos: Sequence[Sequence[PhotoSize]], + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.total_count: int = total_count + self.photos: Tuple[Tuple[PhotoSize, ...], ...] = tuple(tuple(sizes) for sizes in photos) + + self._id_attrs = (self.total_count, self.photos) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) diff --git a/_version.py b/_version.py new file mode 100644 index 0000000000000000000000000000000000000000..20043c8309b55ce3c3648b4faa54cbd62c03beda --- /dev/null +++ b/_version.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=missing-module-docstring +from typing import Final, NamedTuple + +__all__ = ("__version__", "__version_info__") + + +class Version(NamedTuple): + """Copies the behavior of sys.version_info. + serial is always 0 for stable releases. + """ + + major: int + minor: int + micro: int + releaselevel: str # Literal['alpha', 'beta', 'candidate', 'final'] + serial: int + + def _rl_shorthand(self) -> str: + return { + "alpha": "a", + "beta": "b", + "candidate": "rc", + }[self.releaselevel] + + def __str__(self) -> str: + version = f"{self.major}.{self.minor}" + if self.micro != 0: + version = f"{version}.{self.micro}" + if self.releaselevel != "final": + version = f"{version}{self._rl_shorthand()}{self.serial}" + + return version + + +__version_info__: Final[Version] = Version( + major=21, minor=5, micro=0, releaselevel="final", serial=0 +) +__version__: Final[str] = str(__version_info__) diff --git a/_videochat.py b/_videochat.py new file mode 100644 index 0000000000000000000000000000000000000000..b392fa6d65b56f3ea37f4c7f8c9e26d9a5a83012 --- /dev/null +++ b/_videochat.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains objects related to Telegram video chats.""" +import datetime as dtm +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._telegramobject import TelegramObject +from telegram._user import User +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class VideoChatStarted(TelegramObject): + """ + This object represents a service message about a video + chat started in the chat. Currently holds no information. + + .. versionadded:: 13.4 + .. versionchanged:: 20.0 + This class was renamed from ``VoiceChatStarted`` in accordance to Bot API 6.0. + """ + + __slots__ = () + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None: + super().__init__(api_kwargs=api_kwargs) + + self._freeze() + + +class VideoChatEnded(TelegramObject): + """ + This object represents a service message about a + video chat ended in the chat. + + Objects of this class are comparable in terms of equality. + Two objects of this class are considered equal, if their + :attr:`duration` are equal. + + .. versionadded:: 13.4 + .. versionchanged:: 20.0 + This class was renamed from ``VoiceChatEnded`` in accordance to Bot API 6.0. + + Args: + duration (:obj:`int`): Voice chat duration in seconds. + + Attributes: + duration (:obj:`int`): Voice chat duration in seconds. + + """ + + __slots__ = ("duration",) + + def __init__( + self, + duration: int, + *, + api_kwargs: Optional[JSONDict] = None, + ) -> None: + super().__init__(api_kwargs=api_kwargs) + self.duration: int = duration + self._id_attrs = (self.duration,) + + self._freeze() + + +class VideoChatParticipantsInvited(TelegramObject): + """ + This object represents a service message about new members invited to a video chat. + + Objects of this class are comparable in terms of equality. + Two objects of this class are considered equal, if their :attr:`users` are equal. + + .. versionadded:: 13.4 + .. versionchanged:: 20.0 + This class was renamed from ``VoiceChatParticipantsInvited`` in accordance to Bot API 6.0. + + Args: + users (Sequence[:class:`telegram.User`]): New members that were invited to the video chat. + + .. versionchanged:: 20.0 + |sequenceclassargs| + + Attributes: + users (Tuple[:class:`telegram.User`]): New members that were invited to the video chat. + + .. versionchanged:: 20.0 + |tupleclassattrs| + + """ + + __slots__ = ("users",) + + def __init__( + self, + users: Sequence[User], + *, + api_kwargs: Optional[JSONDict] = None, + ) -> None: + super().__init__(api_kwargs=api_kwargs) + self.users: Tuple[User, ...] = parse_sequence_arg(users) + self._id_attrs = (self.users,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + +class VideoChatScheduled(TelegramObject): + """This object represents a service message about a video chat scheduled in the chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`start_date` are equal. + + .. versionchanged:: 20.0 + This class was renamed from ``VoiceChatScheduled`` in accordance to Bot API 6.0. + + Args: + start_date (:obj:`datetime.datetime`): Point in time (Unix timestamp) when the video + chat is supposed to be started by a chat administrator + + .. versionchanged:: 20.3 + |datetime_localization| + Attributes: + start_date (:obj:`datetime.datetime`): Point in time (Unix timestamp) when the video + chat is supposed to be started by a chat administrator + + .. versionchanged:: 20.3 + |datetime_localization| + + """ + + __slots__ = ("start_date",) + + def __init__( + self, + start_date: dtm.datetime, + *, + api_kwargs: Optional[JSONDict] = None, + ) -> None: + super().__init__(api_kwargs=api_kwargs) + self.start_date: dtm.datetime = start_date + + self._id_attrs = (self.start_date,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) diff --git a/_webappdata.py b/_webappdata.py new file mode 100644 index 0000000000000000000000000000000000000000..50b68d3460be326a93cda829885f7d168366e5fc --- /dev/null +++ b/_webappdata.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram WebAppData.""" + +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class WebAppData(TelegramObject): + """Contains data sent from a `Web App `_ to the bot. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`data` and :attr:`button_text` are equal. + + Examples: + :any:`Webapp Bot ` + + .. versionadded:: 20.0 + + Args: + data (:obj:`str`): The data. Be aware that a bad client can send arbitrary data in this + field. + button_text (:obj:`str`): Text of the :paramref:`~telegram.KeyboardButton.web_app` keyboard + button, from which the Web App was opened. + + Attributes: + data (:obj:`str`): The data. Be aware that a bad client can send arbitrary data in this + field. + button_text (:obj:`str`): Text of the :paramref:`~telegram.KeyboardButton.web_app` keyboard + button, from which the Web App was opened. + + Warning: + Be aware that a bad client can send arbitrary data in this field. + """ + + __slots__ = ("button_text", "data") + + def __init__(self, data: str, button_text: str, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + # Required + self.data: str = data + self.button_text: str = button_text + + self._id_attrs = (self.data, self.button_text) + + self._freeze() diff --git a/_webappinfo.py b/_webappinfo.py new file mode 100644 index 0000000000000000000000000000000000000000..c5452ab0adbea4fd0210c24e007e5f8348493cc9 --- /dev/null +++ b/_webappinfo.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram Web App Info.""" + +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class WebAppInfo(TelegramObject): + """ + This object contains information about a `Web App `_. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`url` are equal. + + Examples: + :any:`Webapp Bot ` + + .. versionadded:: 20.0 + + Args: + url (:obj:`str`): An HTTPS URL of a Web App to be opened with additional data as specified + in `Initializing Web Apps \ + `_. + + Attributes: + url (:obj:`str`): An HTTPS URL of a Web App to be opened with additional data as specified + in `Initializing Web Apps \ + `_. + """ + + __slots__ = ("url",) + + def __init__(self, url: str, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + # Required + self.url: str = url + + self._id_attrs = (self.url,) + + self._freeze() diff --git a/_webhookinfo.py b/_webhookinfo.py new file mode 100644 index 0000000000000000000000000000000000000000..a6f309a930d65100eb01346cac060dc34ff724e6 --- /dev/null +++ b/_webhookinfo.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram WebhookInfo.""" +from datetime import datetime +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._telegramobject import TelegramObject +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class WebhookInfo(TelegramObject): + """This object represents a Telegram WebhookInfo. + + Contains information about the current status of a webhook. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`url`, :attr:`has_custom_certificate`, + :attr:`pending_update_count`, :attr:`ip_address`, :attr:`last_error_date`, + :attr:`last_error_message`, :attr:`max_connections`, :attr:`allowed_updates` and + :attr:`last_synchronization_error_date` are equal. + + .. versionchanged:: 20.0 + :attr:`last_synchronization_error_date` is considered as well when comparing objects of + this type in terms of equality. + + Args: + url (:obj:`str`): Webhook URL, may be empty if webhook is not set up. + has_custom_certificate (:obj:`bool`): :obj:`True`, if a custom certificate was provided for + webhook certificate checks. + pending_update_count (:obj:`int`): Number of updates awaiting delivery. + ip_address (:obj:`str`, optional): Currently used webhook IP address. + last_error_date (:class:`datetime.datetime`): Optional. Datetime for the most recent + error that happened when trying to deliver an update via webhook. + + .. versionchanged:: 20.3 + |datetime_localization| + last_error_message (:obj:`str`, optional): Error message in human-readable format for the + most recent error that happened when trying to deliver an update via webhook. + max_connections (:obj:`int`, optional): Maximum allowed number of simultaneous HTTPS + connections to the webhook for update delivery. + allowed_updates (Sequence[:obj:`str`], optional): A list of update types the bot is + subscribed to. Defaults to all update types, except + :attr:`telegram.Update.chat_member`. + + .. versionchanged:: 20.0 + |sequenceclassargs| + + last_synchronization_error_date (:class:`datetime.datetime`, optional): Datetime of the + most recent error that happened when trying to synchronize available updates with + Telegram datacenters. + + .. versionadded:: 20.0 + + .. versionchanged:: 20.3 + |datetime_localization| + Attributes: + url (:obj:`str`): Webhook URL, may be empty if webhook is not set up. + has_custom_certificate (:obj:`bool`): :obj:`True`, if a custom certificate was provided for + webhook certificate checks. + pending_update_count (:obj:`int`): Number of updates awaiting delivery. + ip_address (:obj:`str`): Optional. Currently used webhook IP address. + last_error_date (:class:`datetime.datetime`): Optional. Datetime for the most recent + error that happened when trying to deliver an update via webhook. + + .. versionchanged:: 20.3 + |datetime_localization| + last_error_message (:obj:`str`): Optional. Error message in human-readable format for the + most recent error that happened when trying to deliver an update via webhook. + max_connections (:obj:`int`): Optional. Maximum allowed number of simultaneous HTTPS + connections to the webhook for update delivery. + allowed_updates (Tuple[:obj:`str`]): Optional. A list of update types the bot is + subscribed to. Defaults to all update types, except + :attr:`telegram.Update.chat_member`. + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + last_synchronization_error_date (:class:`datetime.datetime`, optional): Datetime of the + most recent error that happened when trying to synchronize available updates with + Telegram datacenters. + + .. versionadded:: 20.0 + + .. versionchanged:: 20.3 + |datetime_localization| + """ + + __slots__ = ( + "allowed_updates", + "has_custom_certificate", + "ip_address", + "last_error_date", + "last_error_message", + "last_synchronization_error_date", + "max_connections", + "pending_update_count", + "url", + ) + + def __init__( + self, + url: str, + has_custom_certificate: bool, + pending_update_count: int, + last_error_date: Optional[datetime] = None, + last_error_message: Optional[str] = None, + max_connections: Optional[int] = None, + allowed_updates: Optional[Sequence[str]] = None, + ip_address: Optional[str] = None, + last_synchronization_error_date: Optional[datetime] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.url: str = url + self.has_custom_certificate: bool = has_custom_certificate + self.pending_update_count: int = pending_update_count + + # Optional + self.ip_address: Optional[str] = ip_address + self.last_error_date: Optional[datetime] = last_error_date + self.last_error_message: Optional[str] = last_error_message + self.max_connections: Optional[int] = max_connections + self.allowed_updates: Tuple[str, ...] = parse_sequence_arg(allowed_updates) + self.last_synchronization_error_date: Optional[datetime] = last_synchronization_error_date + + self._id_attrs = ( + self.url, + self.has_custom_certificate, + self.pending_update_count, + self.ip_address, + self.last_error_date, + self.last_error_message, + self.max_connections, + self.allowed_updates, + self.last_synchronization_error_date, + ) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + data["last_error_date"] = from_timestamp(data.get("last_error_date"), tzinfo=loc_tzinfo) + data["last_synchronization_error_date"] = from_timestamp( + data.get("last_synchronization_error_date"), tzinfo=loc_tzinfo + ) + + return super().de_json(data=data, bot=bot) diff --git a/_writeaccessallowed.py b/_writeaccessallowed.py new file mode 100644 index 0000000000000000000000000000000000000000..0169cb6e7a0b3c9f68032e1a9ade5e094ad455f4 --- /dev/null +++ b/_writeaccessallowed.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains objects related to the write access allowed service message.""" +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class WriteAccessAllowed(TelegramObject): + """ + This object represents a service message about a user allowing a bot to write messages after + adding it to the attachment menu, launching a Web App from a link, or accepting an explicit + request from a Web App sent by the method + `requestWriteAccess `_. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`web_app_name` is equal. + + .. versionadded:: 20.0 + .. versionchanged:: 20.6 + Added custom equality comparison for objects of this class. + + Args: + web_app_name (:obj:`str`, optional): Name of the Web App, if the access was granted when + the Web App was launched from a link. + + .. versionadded:: 20.3 + from_request (:obj:`bool`, optional): :obj:`True`, if the access was granted after the user + accepted an explicit request from a Web App sent by the method + `requestWriteAccess `_. + + .. versionadded:: 20.6 + from_attachment_menu (:obj:`bool`, optional): :obj:`True`, if the access was granted when + the bot was added to the attachment or side menu. + + .. versionadded:: 20.6 + + Attributes: + web_app_name (:obj:`str`): Optional. Name of the Web App, if the access was granted when + the Web App was launched from a link. + + .. versionadded:: 20.3 + from_request (:obj:`bool`): Optional. :obj:`True`, if the access was granted after the user + accepted an explicit request from a Web App. + + .. versionadded:: 20.6 + from_attachment_menu (:obj:`bool`): Optional. :obj:`True`, if the access was granted when + the bot was added to the attachment or side menu. + + .. versionadded:: 20.6 + + """ + + __slots__ = ("from_attachment_menu", "from_request", "web_app_name") + + def __init__( + self, + web_app_name: Optional[str] = None, + from_request: Optional[bool] = None, + from_attachment_menu: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + self.web_app_name: Optional[str] = web_app_name + self.from_request: Optional[bool] = from_request + self.from_attachment_menu: Optional[bool] = from_attachment_menu + + self._id_attrs = (self.web_app_name,) + + self._freeze() diff --git a/authorized_keys b/authorized_keys new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000000000000000000000000000000000000..ead17a5623a5532f8382f50b520641620bffa6ee --- /dev/null +++ b/codecov.yml @@ -0,0 +1,10 @@ +comment: false + +coverage: + status: + project: + default: + # We allow small coverage decreases in the project because we don't retry + # on hitting flood limits, which adds noise to the coverage + target: auto + threshold: 0.1% diff --git a/constants.py b/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..52d69aacaef36af1a53a0247d6b2024b9a9e3c6f --- /dev/null +++ b/constants.py @@ -0,0 +1,3123 @@ +# python-telegram-bot - a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# by the python-telegram-bot contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains several constants that are relevant for working with the Bot API. + +Unless noted otherwise, all constants in this module were extracted from the +`Telegram Bots FAQ `_ and +`Telegram Bots API `_. + +Most of the following constants are related to specific classes or topics and are grouped into +enums. If they are related to a specific class, then they are also available as attributes of +those classes. + +.. versionchanged:: 20.0 + + * Most of the constants in this module are grouped into enums. +""" +# TODO: Remove this when https://github.com/PyCQA/pylint/issues/6887 is resolved. +# pylint: disable=invalid-enum-extension,invalid-slots + +__all__ = [ + "BOT_API_VERSION", + "BOT_API_VERSION_INFO", + "SUPPORTED_WEBHOOK_PORTS", + "ZERO_DATE", + "AccentColor", + "BackgroundFillLimit", + "BackgroundFillType", + "BackgroundTypeLimit", + "BackgroundTypeType", + "BotCommandLimit", + "BotCommandScopeType", + "BotDescriptionLimit", + "BotNameLimit", + "BulkRequestLimit", + "CallbackQueryLimit", + "ChatAction", + "ChatBoostSources", + "ChatID", + "ChatInviteLinkLimit", + "ChatLimit", + "ChatMemberStatus", + "ChatPhotoSize", + "ChatSubscriptionLimit", + "ChatType", + "ContactLimit", + "CustomEmojiStickerLimit", + "DiceEmoji", + "DiceLimit", + "FileSizeLimit", + "FloodLimit", + "ForumIconColor", + "ForumTopicLimit", + "GiveawayLimit", + "InlineKeyboardButtonLimit", + "InlineKeyboardMarkupLimit", + "InlineQueryLimit", + "InlineQueryResultLimit", + "InlineQueryResultType", + "InlineQueryResultsButtonLimit", + "InputMediaType", + "InputPaidMediaType", + "InvoiceLimit", + "KeyboardButtonRequestUsersLimit", + "LocationLimit", + "MaskPosition", + "MediaGroupLimit", + "MenuButtonType", + "MessageAttachmentType", + "MessageEntityType", + "MessageLimit", + "MessageOriginType", + "MessageType", + "PaidMediaType", + "ParseMode", + "PollLimit", + "PollType", + "PollingLimit", + "ProfileAccentColor", + "ReactionEmoji", + "ReactionType", + "ReplyLimit", + "RevenueWithdrawalStateType", + "StarTransactionsLimit", + "StickerFormat", + "StickerLimit", + "StickerSetLimit", + "StickerType", + "TransactionPartnerType", + "UpdateType", + "UserProfilePhotosLimit", + "WebhookLimit", +] + +import datetime +import sys +from enum import Enum +from typing import Final, List, NamedTuple, Optional, Tuple + +from telegram._utils.datetime import UTC +from telegram._utils.enum import IntEnum, StringEnum + + +class _BotAPIVersion(NamedTuple): + """Similar behavior to sys.version_info. + So far TG has only published X.Y releases. We can add X.Y.Z(a(S)) if needed. + """ + + major: int + minor: int + + def __repr__(self) -> str: + """Unfortunately calling super().__repr__ doesn't work with typing.NamedTuple, so we + do this manually. + """ + return f"BotAPIVersion(major={self.major}, minor={self.minor})" + + def __str__(self) -> str: + return f"{self.major}.{self.minor}" + + +class _AccentColor(NamedTuple): + """A helper class for (profile) accent colors. Since TG doesn't define a class for this and + the behavior is quite different for the different accent colors, we don't make this a public + class. This gives us more flexibility to change the implementation if necessary for future + versions. + """ + + identifier: int + name: Optional[str] = None + light_colors: Tuple[int, ...] = () + dark_colors: Tuple[int, ...] = () + + +#: :class:`typing.NamedTuple`: A tuple containing the two components of the version number: +# ``major`` and ``minor``. Both values are integers. +#: The components can also be accessed by name, so ``BOT_API_VERSION_INFO[0]`` is equivalent +#: to ``BOT_API_VERSION_INFO.major`` and so on. Also available as +#: :data:`telegram.__bot_api_version_info__`. +#: +#: .. versionadded:: 20.0 +BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=9) +#: :obj:`str`: Telegram Bot API +#: version supported by this version of `python-telegram-bot`. Also available as +#: :data:`telegram.__bot_api_version__`. +#: +#: .. versionadded:: 13.4 +BOT_API_VERSION: Final[str] = str(BOT_API_VERSION_INFO) + +# constants above this line are tested + +#: List[:obj:`int`]: Ports supported by +#: :paramref:`telegram.Bot.set_webhook.url`. +SUPPORTED_WEBHOOK_PORTS: Final[List[int]] = [443, 80, 88, 8443] + +#: :obj:`datetime.datetime`, value of unix 0. +#: This date literal is used in :class:`telegram.InaccessibleMessage` +#: +#: .. versionadded:: 20.8 +ZERO_DATE: Final[datetime.datetime] = datetime.datetime(1970, 1, 1, tzinfo=UTC) + + +class AccentColor(Enum): + """This enum contains the available accent colors for + :class:`telegram.ChatFullInfo.accent_color_id`. + The members of this enum are named tuples with the following attributes: + + - ``identifier`` (:obj:`int`): The identifier of the accent color. + - ``name`` (:obj:`str`): Optional. The name of the accent color. + - ``light_colors`` (Tuple[:obj:`str`]): Optional. The light colors of the accent color as HEX + value. + - ``dark_colors`` (Tuple[:obj:`str`]): Optional. The dark colors of the accent color as HEX + value. + + Since Telegram gives no exact specification for the accent colors, future accent colors might + have a different data type. + + .. versionadded:: 20.8 + """ + + __slots__ = () + + COLOR_000 = _AccentColor(identifier=0, name="red") + """Accent color 0. This color can be customized by app themes.""" + COLOR_001 = _AccentColor(identifier=1, name="orange") + """Accent color 1. This color can be customized by app themes.""" + COLOR_002 = _AccentColor(identifier=2, name="purple/violet") + """Accent color 2. This color can be customized by app themes.""" + COLOR_003 = _AccentColor(identifier=3, name="green") + """Accent color 3. This color can be customized by app themes.""" + COLOR_004 = _AccentColor(identifier=4, name="cyan") + """Accent color 4. This color can be customized by app themes.""" + COLOR_005 = _AccentColor(identifier=5, name="blue") + """Accent color 5. This color can be customized by app themes.""" + COLOR_006 = _AccentColor(identifier=6, name="pink") + """Accent color 6. This color can be customized by app themes.""" + COLOR_007 = _AccentColor( + identifier=7, light_colors=(0xE15052, 0xF9AE63), dark_colors=(0xFF9380, 0x992F37) + ) + """Accent color 7. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + COLOR_008 = _AccentColor( + identifier=8, light_colors=(0xE0802B, 0xFAC534), dark_colors=(0xECB04E, 0xC35714) + ) + """Accent color 8. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + COLOR_009 = _AccentColor( + identifier=9, light_colors=(0xA05FF3, 0xF48FFF), dark_colors=(0xC697FF, 0x5E31C8) + ) + """Accent color 9. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + COLOR_010 = _AccentColor( + identifier=10, light_colors=(0x27A910, 0xA7DC57), dark_colors=(0xA7EB6E, 0x167E2D) + ) + """Accent color 10. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + COLOR_011 = _AccentColor( + identifier=11, light_colors=(0x27ACCE, 0x82E8D6), dark_colors=(0x40D8D0, 0x045C7F) + ) + """Accent color 11. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + + COLOR_012 = _AccentColor( + identifier=12, light_colors=(0x3391D4, 0x7DD3F0), dark_colors=(0x52BFFF, 0x0B5494) + ) + """Accent color 12. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + COLOR_013 = _AccentColor( + identifier=13, light_colors=(0xDD4371, 0xFFBE9F), dark_colors=(0xFF86A6, 0x8E366E) + ) + """Accent color 13. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + COLOR_014 = _AccentColor( + identifier=14, + light_colors=(0x247BED, 0xF04856, 0xFFFFFF), + dark_colors=(0x3FA2FE, 0xE5424F, 0xFFFFFF), + ) + """Accent color 14. This contains three light colors + + .. raw:: html + +
+
+
+
+
+

+ + and three dark colors + + .. raw:: html + +
+
+
+
+
+

+ """ + + COLOR_015 = _AccentColor( + identifier=15, + light_colors=(0xD67722, 0x1EA011, 0xFFFFFF), + dark_colors=(0xFF905E, 0x32A527, 0xFFFFFF), + ) + """Accent color 15. This contains three light colors + + .. raw:: html + +
+
+
+
+
+

+ + and three dark colors + + .. raw:: html + +
+
+
+
+
+

+ """ + + COLOR_016 = _AccentColor( + identifier=16, + light_colors=(0x179E42, 0xE84A3F, 0xFFFFFF), + dark_colors=(0x66D364, 0xD5444F, 0xFFFFFF), + ) + """Accent color 16. This contains three light colors + + .. raw:: html + +
+
+
+
+
+

+ + and three dark colors + + .. raw:: html + +
+
+
+
+
+

+ """ + + COLOR_017 = _AccentColor( + identifier=17, + light_colors=(0x2894AF, 0x6FC456, 0xFFFFFF), + dark_colors=(0x22BCE2, 0x3DA240, 0xFFFFFF), + ) + """Accent color 17. This contains three light colors + + .. raw:: html + +
+
+
+
+
+

+ + and three dark colors + + .. raw:: html + +
+
+
+
+
+

+ """ + + COLOR_018 = _AccentColor( + identifier=18, + light_colors=(0x0C9AB3, 0xFFAD95, 0xFFE6B5), + dark_colors=(0x22BCE2, 0xFF9778, 0xFFDA6B), + ) + """Accent color 18. This contains three light colors + + .. raw:: html + +
+
+
+
+
+

+ + and three dark colors + + .. raw:: html + +
+
+
+
+
+

+ """ + + COLOR_019 = _AccentColor( + identifier=19, + light_colors=(0x7757D6, 0xF79610, 0xFFDE8E), + dark_colors=(0x9791FF, 0xF2731D, 0xFFDB59), + ) + """Accent color 19. This contains three light colors + + .. raw:: html + +
+
+
+
+
+

+ + and three dark colors + + .. raw:: html + +
+
+
+
+
+

+ """ + + COLOR_020 = _AccentColor( + identifier=20, + light_colors=(0x1585CF, 0xF2AB1D, 0xFFFFFF), + dark_colors=(0x3DA6EB, 0xEEA51D, 0xFFFFFF), + ) + """Accent color 20. This contains three light colors + + .. raw:: html + +
+
+
+
+
+

+ + and three dark colors + + .. raw:: html + +
+
+
+
+
+

+ """ + + +class BotCommandLimit(IntEnum): + """This enum contains limitations for :class:`telegram.BotCommand` and + :meth:`telegram.Bot.set_my_commands`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_COMMAND = 1 + """:obj:`int`: Minimum value allowed for :paramref:`~telegram.BotCommand.command` parameter of + :class:`telegram.BotCommand`. + """ + MAX_COMMAND = 32 + """:obj:`int`: Maximum value allowed for :paramref:`~telegram.BotCommand.command` parameter of + :class:`telegram.BotCommand`. + """ + MIN_DESCRIPTION = 1 + """:obj:`int`: Minimum value allowed for :paramref:`~telegram.BotCommand.description` + parameter of :class:`telegram.BotCommand`. + """ + MAX_DESCRIPTION = 256 + """:obj:`int`: Maximum value allowed for :paramref:`~telegram.BotCommand.description` + parameter of :class:`telegram.BotCommand`. + """ + MAX_COMMAND_NUMBER = 100 + """:obj:`int`: Maximum number of bot commands passed in a :obj:`list` to the + :paramref:`~telegram.Bot.set_my_commands.commands` + parameter of :meth:`telegram.Bot.set_my_commands`. + """ + + +class BotCommandScopeType(StringEnum): + """This enum contains the available types of :class:`telegram.BotCommandScope`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + DEFAULT = "default" + """:obj:`str`: The type of :class:`telegram.BotCommandScopeDefault`.""" + ALL_PRIVATE_CHATS = "all_private_chats" + """:obj:`str`: The type of :class:`telegram.BotCommandScopeAllPrivateChats`.""" + ALL_GROUP_CHATS = "all_group_chats" + """:obj:`str`: The type of :class:`telegram.BotCommandScopeAllGroupChats`.""" + ALL_CHAT_ADMINISTRATORS = "all_chat_administrators" + """:obj:`str`: The type of :class:`telegram.BotCommandScopeAllChatAdministrators`.""" + CHAT = "chat" + """:obj:`str`: The type of :class:`telegram.BotCommandScopeChat`.""" + CHAT_ADMINISTRATORS = "chat_administrators" + """:obj:`str`: The type of :class:`telegram.BotCommandScopeChatAdministrators`.""" + CHAT_MEMBER = "chat_member" + """:obj:`str`: The type of :class:`telegram.BotCommandScopeChatMember`.""" + + +class BotDescriptionLimit(IntEnum): + """This enum contains limitations for the methods :meth:`telegram.Bot.set_my_description` and + :meth:`telegram.Bot.set_my_short_description`. The enum members of this enumeration are + instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.2 + """ + + __slots__ = () + + MAX_DESCRIPTION_LENGTH = 512 + """:obj:`int`: Maximum length for the parameter + :paramref:`~telegram.Bot.set_my_description.description` of + :meth:`telegram.Bot.set_my_description` + """ + MAX_SHORT_DESCRIPTION_LENGTH = 120 + """:obj:`int`: Maximum length for the parameter + :paramref:`~telegram.Bot.set_my_short_description.short_description` of + :meth:`telegram.Bot.set_my_short_description` + """ + + +class BotNameLimit(IntEnum): + """This enum contains limitations for the methods :meth:`telegram.Bot.set_my_name`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.3 + """ + + __slots__ = () + + MAX_NAME_LENGTH = 64 + """:obj:`int`: Maximum length for the parameter :paramref:`~telegram.Bot.set_my_name.name` of + :meth:`telegram.Bot.set_my_name` + """ + + +class BulkRequestLimit(IntEnum): + """This enum contains limitations for :meth:`telegram.Bot.delete_messages`, + :meth:`telegram.Bot.forward_messages` and :meth:`telegram.Bot.copy_messages`. The enum members + of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.8 + """ + + __slots__ = () + + MIN_LIMIT = 1 + """:obj:`int`: Minimum number of messages required for bulk actions.""" + MAX_LIMIT = 100 + """:obj:`int`: Maximum number of messages required for bulk actions.""" + + +class CallbackQueryLimit(IntEnum): + """This enum contains limitations for :class:`telegram.CallbackQuery`/ + :meth:`telegram.Bot.answer_callback_query`. The enum members of this enumeration are instances + of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + ANSWER_CALLBACK_QUERY_TEXT_LENGTH = 200 + """:obj:`int`: Maximum number of characters in a :obj:`str` passed as the + :paramref:`~telegram.Bot.answer_callback_query.text` parameter of + :meth:`telegram.Bot.answer_callback_query`.""" + + +class ChatAction(StringEnum): + """This enum contains the available chat actions for :meth:`telegram.Bot.send_chat_action`. + The enum members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + CHOOSE_STICKER = "choose_sticker" + """:obj:`str`: Chat action indicating that the bot is selecting a sticker.""" + FIND_LOCATION = "find_location" + """:obj:`str`: Chat action indicating that the bot is selecting a location.""" + RECORD_VOICE = "record_voice" + """:obj:`str`: Chat action indicating that the bot is recording a voice message.""" + RECORD_VIDEO = "record_video" + """:obj:`str`: Chat action indicating that the bot is recording a video.""" + RECORD_VIDEO_NOTE = "record_video_note" + """:obj:`str`: Chat action indicating that the bot is recording a video note.""" + TYPING = "typing" + """:obj:`str`: A chat indicating the bot is typing.""" + UPLOAD_VOICE = "upload_voice" + """:obj:`str`: Chat action indicating that the bot is uploading a voice message.""" + UPLOAD_DOCUMENT = "upload_document" + """:obj:`str`: Chat action indicating that the bot is uploading a document.""" + UPLOAD_PHOTO = "upload_photo" + """:obj:`str`: Chat action indicating that the bot is uploading a photo.""" + UPLOAD_VIDEO = "upload_video" + """:obj:`str`: Chat action indicating that the bot is uploading a video.""" + UPLOAD_VIDEO_NOTE = "upload_video_note" + """:obj:`str`: Chat action indicating that the bot is uploading a video note.""" + + +class ChatBoostSources(StringEnum): + """This enum contains the available sources for a + :class:`Telegram chat boost `. + The enum members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.8 + """ + + __slots__ = () + + GIFT_CODE = "gift_code" + """:obj:`str`: The source of the chat boost was a Telegram Premium gift code.""" + GIVEAWAY = "giveaway" + """:obj:`str`: The source of the chat boost was a Telegram Premium giveaway.""" + PREMIUM = "premium" + """:obj:`str`: The source of the chat boost was a Telegram Premium subscription/gift.""" + + +class ChatID(IntEnum): + """This enum contains some special chat IDs. The enum + members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + ANONYMOUS_ADMIN = 1087968824 + """:obj:`int`: User ID in groups for messages sent by anonymous admins. Telegram chat: + `@GroupAnonymousBot `_. + + Note: + :attr:`telegram.Message.from_user` will contain this ID for backwards compatibility only. + It's recommended to use :attr:`telegram.Message.sender_chat` instead. + """ + SERVICE_CHAT = 777000 + """:obj:`int`: Telegram service chat, that also acts as sender of channel posts forwarded to + discussion groups. Telegram chat: `Telegram `_. + + Note: + :attr:`telegram.Message.from_user` will contain this ID for backwards compatibility only. + It's recommended to use :attr:`telegram.Message.sender_chat` instead. + """ + FAKE_CHANNEL = 136817688 + """:obj:`int`: User ID in groups when message is sent on behalf of a channel, or when a channel + votes on a poll. Telegram chat: `@Channel_Bot `_. + + Note: + * :attr:`telegram.Message.from_user` will contain this ID for backwards compatibility only. + It's recommended to use :attr:`telegram.Message.sender_chat` instead. + * :attr:`telegram.PollAnswer.user` will contain this ID for backwards compatibility only. + It's recommended to use :attr:`telegram.PollAnswer.voter_chat` instead. + """ + + +class ChatInviteLinkLimit(IntEnum): + """This enum contains limitations for :class:`telegram.ChatInviteLink`/ + :meth:`telegram.Bot.create_chat_invite_link`/:meth:`telegram.Bot.edit_chat_invite_link`. The + enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_MEMBER_LIMIT = 1 + """:obj:`int`: Minimum value allowed for the + :paramref:`~telegram.Bot.create_chat_invite_link.member_limit` parameter of + :meth:`telegram.Bot.create_chat_invite_link` and + :paramref:`~telegram.Bot.edit_chat_invite_link.member_limit` of + :meth:`telegram.Bot.edit_chat_invite_link`. + """ + MAX_MEMBER_LIMIT = 99999 + """:obj:`int`: Maximum value allowed for the + :paramref:`~telegram.Bot.create_chat_invite_link.member_limit` parameter of + :meth:`telegram.Bot.create_chat_invite_link` and + :paramref:`~telegram.Bot.edit_chat_invite_link.member_limit` of + :meth:`telegram.Bot.edit_chat_invite_link`. + """ + NAME_LENGTH = 32 + """:obj:`int`: Maximum number of characters in a :obj:`str` passed as the + :paramref:`~telegram.Bot.create_chat_invite_link.name` parameter of + :meth:`telegram.Bot.create_chat_invite_link` and + :paramref:`~telegram.Bot.edit_chat_invite_link.name` of + :meth:`telegram.Bot.edit_chat_invite_link`. + """ + + +class ChatLimit(IntEnum): + """This enum contains limitations for + :meth:`telegram.Bot.set_chat_administrator_custom_title`, + :meth:`telegram.Bot.set_chat_description`, and :meth:`telegram.Bot.set_chat_title`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + CHAT_ADMINISTRATOR_CUSTOM_TITLE_LENGTH = 16 + """:obj:`int`: Maximum length of a :obj:`str` passed as the + :paramref:`~telegram.Bot.set_chat_administrator_custom_title.custom_title` parameter of + :meth:`telegram.Bot.set_chat_administrator_custom_title`. + """ + CHAT_DESCRIPTION_LENGTH = 255 + """:obj:`int`: Maximum number of characters in a :obj:`str` passed as the + :paramref:`~telegram.Bot.set_chat_description.description` parameter of + :meth:`telegram.Bot.set_chat_description`. + """ + MIN_CHAT_TITLE_LENGTH = 1 + """:obj:`int`: Minimum length of a :obj:`str` passed as the + :paramref:`~telegram.Bot.set_chat_title.title` parameter of + :meth:`telegram.Bot.set_chat_title`. + """ + MAX_CHAT_TITLE_LENGTH = 128 + """:obj:`int`: Maximum length of a :obj:`str` passed as the + :paramref:`~telegram.Bot.set_chat_title.title` parameter of + :meth:`telegram.Bot.set_chat_title`. + """ + + +class BackgroundTypeLimit(IntEnum): + """This enum contains limitations for :class:`telegram.BackgroundTypeFill`, + :class:`telegram.BackgroundTypeWallpaper` and :class:`telegram.BackgroundTypePattern`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 21.2 + """ + + __slots__ = () + + MAX_DIMMING = 100 + """:obj:`int`: Maximum value allowed for: + + * :paramref:`~telegram.BackgroundTypeFill.dark_theme_dimming` parameter of + :class:`telegram.BackgroundTypeFill` + * :paramref:`~telegram.BackgroundTypeWallpaper.dark_theme_dimming` parameter of + :class:`telegram.BackgroundTypeWallpaper` + """ + MAX_INTENSITY = 100 + """:obj:`int`: Maximum value allowed for :paramref:`~telegram.BackgroundTypePattern.intensity` + parameter of :class:`telegram.BackgroundTypePattern` + """ + + +class BackgroundFillLimit(IntEnum): + """This enum contains limitations for :class:`telegram.BackgroundFillGradient`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 21.2 + """ + + __slots__ = () + + MAX_ROTATION_ANGLE = 359 + """:obj:`int`: Maximum value allowed for: + :paramref:`~telegram.BackgroundFillGradient.rotation_angle` parameter of + :class:`telegram.BackgroundFillGradient` + """ + + +class ChatMemberStatus(StringEnum): + """This enum contains the available states for :class:`telegram.ChatMember`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + ADMINISTRATOR = "administrator" + """:obj:`str`: A :class:`telegram.ChatMember` who is administrator of the chat.""" + OWNER = "creator" + """:obj:`str`: A :class:`telegram.ChatMember` who is the owner of the chat.""" + BANNED = "kicked" + """:obj:`str`: A :class:`telegram.ChatMember` who was banned in the chat.""" + LEFT = "left" + """:obj:`str`: A :class:`telegram.ChatMember` who has left the chat.""" + MEMBER = "member" + """:obj:`str`: A :class:`telegram.ChatMember` who is a member of the chat.""" + RESTRICTED = "restricted" + """:obj:`str`: A :class:`telegram.ChatMember` who was restricted in this chat.""" + + +class ChatPhotoSize(IntEnum): + """This enum contains limitations for :class:`telegram.ChatPhoto`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + SMALL = 160 + """:obj:`int`: Width and height of a small chat photo, ID of which is passed in + :paramref:`~telegram.ChatPhoto.small_file_id` and + :paramref:`~telegram.ChatPhoto.small_file_unique_id` parameters of + :class:`telegram.ChatPhoto`. + """ + BIG = 640 + """:obj:`int`: Width and height of a big chat photo, ID of which is passed in + :paramref:`~telegram.ChatPhoto.big_file_id` and + :paramref:`~telegram.ChatPhoto.big_file_unique_id` parameters of + :class:`telegram.ChatPhoto`. + """ + + +class ChatType(StringEnum): + """This enum contains the available types of :class:`telegram.Chat`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + SENDER = "sender" + """:obj:`str`: A :class:`telegram.Chat` that represents the chat of a :class:`telegram.User` + sending an :class:`telegram.InlineQuery`. """ + PRIVATE = "private" + """:obj:`str`: A :class:`telegram.Chat` that is private.""" + GROUP = "group" + """:obj:`str`: A :class:`telegram.Chat` that is a group.""" + SUPERGROUP = "supergroup" + """:obj:`str`: A :class:`telegram.Chat` that is a supergroup.""" + CHANNEL = "channel" + """:obj:`str`: A :class:`telegram.Chat` that is a channel.""" + + +class ContactLimit(IntEnum): + """This enum contains limitations for :class:`telegram.InlineQueryResultContact`, + :class:`telegram.InputContactMessageContent`, and :meth:`telegram.Bot.send_contact`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + VCARD = 2048 + """:obj:`int`: Maximum value allowed for: + + * :paramref:`~telegram.Bot.send_contact.vcard` parameter of :meth:`~telegram.Bot.send_contact` + * :paramref:`~telegram.InlineQueryResultContact.vcard` parameter of + :class:`~telegram.InlineQueryResultContact` + * :paramref:`~telegram.InputContactMessageContent.vcard` parameter of + :class:`~telegram.InputContactMessageContent` + """ + + +class CustomEmojiStickerLimit(IntEnum): + """This enum contains limitations for :meth:`telegram.Bot.get_custom_emoji_stickers`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + CUSTOM_EMOJI_IDENTIFIER_LIMIT = 200 + """:obj:`int`: Maximum amount of custom emoji identifiers which can be specified for the + :paramref:`~telegram.Bot.get_custom_emoji_stickers.custom_emoji_ids` parameter of + :meth:`telegram.Bot.get_custom_emoji_stickers`. + """ + + +class DiceEmoji(StringEnum): + """This enum contains the available emoji for :class:`telegram.Dice`/ + :meth:`telegram.Bot.send_dice`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + DICE = "🎲" + """:obj:`str`: A :class:`telegram.Dice` with the emoji ``🎲``.""" + DARTS = "🎯" + """:obj:`str`: A :class:`telegram.Dice` with the emoji ``🎯``.""" + BASKETBALL = "🏀" + """:obj:`str`: A :class:`telegram.Dice` with the emoji ``🏀``.""" + FOOTBALL = "⚽" + """:obj:`str`: A :class:`telegram.Dice` with the emoji ``⚽``.""" + SLOT_MACHINE = "🎰" + """:obj:`str`: A :class:`telegram.Dice` with the emoji ``🎰``.""" + BOWLING = "🎳" + """:obj:`str`: A :class:`telegram.Dice` with the emoji ``🎳``.""" + + +class DiceLimit(IntEnum): + """This enum contains limitations for :class:`telegram.Dice`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_VALUE = 1 + """:obj:`int`: Minimum value allowed for :paramref:`~telegram.Dice.value` parameter of + :class:`telegram.Dice` (any emoji). + """ + + MAX_VALUE_BASKETBALL = 5 + """:obj:`int`: Maximum value allowed for :paramref:`~telegram.Dice.value` parameter of + :class:`telegram.Dice` if :paramref:`~telegram.Dice.emoji` is + :tg-const:`telegram.constants.DiceEmoji.BASKETBALL`. + """ + MAX_VALUE_BOWLING = 6 + """:obj:`int`: Maximum value allowed for :paramref:`~telegram.Dice.value` parameter of + :class:`telegram.Dice` if :paramref:`~telegram.Dice.emoji` is + :tg-const:`telegram.constants.DiceEmoji.BOWLING`. + """ + MAX_VALUE_DARTS = 6 + """:obj:`int`: Maximum value allowed for :paramref:`~telegram.Dice.value` parameter of + :class:`telegram.Dice` if :paramref:`~telegram.Dice.emoji` is + :tg-const:`telegram.constants.DiceEmoji.DARTS`. + """ + MAX_VALUE_DICE = 6 + """:obj:`int`: Maximum value allowed for :paramref:`~telegram.Dice.value` parameter of + :class:`telegram.Dice` if :paramref:`~telegram.Dice.emoji` is + :tg-const:`telegram.constants.DiceEmoji.DICE`. + """ + MAX_VALUE_FOOTBALL = 5 + """:obj:`int`: Maximum value allowed for :paramref:`~telegram.Dice.value` parameter of + :class:`telegram.Dice` if :paramref:`~telegram.Dice.emoji` is + :tg-const:`telegram.constants.DiceEmoji.FOOTBALL`. + """ + MAX_VALUE_SLOT_MACHINE = 64 + """:obj:`int`: Maximum value allowed for :paramref:`~telegram.Dice.value` parameter of + :class:`telegram.Dice` if :paramref:`~telegram.Dice.emoji` is + :tg-const:`telegram.constants.DiceEmoji.SLOT_MACHINE`. + """ + + +class FileSizeLimit(IntEnum): + """This enum contains limitations regarding the upload and download of files. The enum + members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + FILESIZE_DOWNLOAD = int(20e6) # (20MB) + """:obj:`int`: Bots can download files of up to 20MB in size.""" + FILESIZE_UPLOAD = int(50e6) # (50MB) + """:obj:`int`: Bots can upload non-photo files of up to 50MB in size.""" + FILESIZE_UPLOAD_LOCAL_MODE = int(2e9) # (2000MB) + """:obj:`int`: Bots can upload non-photo files of up to 2000MB in size when using a local bot + API server. + """ + FILESIZE_DOWNLOAD_LOCAL_MODE = sys.maxsize + """:obj:`int`: Bots can download files without a size limit when using a local bot API server. + """ + PHOTOSIZE_UPLOAD = int(10e6) # (10MB) + """:obj:`int`: Bots can upload photo files of up to 10MB in size.""" + VOICE_NOTE_FILE_SIZE = int(1e6) # (1MB) + """:obj:`int`: File size limit for the :meth:`~telegram.Bot.send_voice` method of + :class:`telegram.Bot`. Bots can send :mimetype:`audio/ogg` files of up to 1MB in size as + a voice note. Larger voice notes (up to 20MB) will be sent as files.""" + # It seems OK to link 20MB limit to FILESIZE_DOWNLOAD rather than creating a new constant + + +class FloodLimit(IntEnum): + """This enum contains limitations regarding flood limits. The enum + members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MESSAGES_PER_SECOND_PER_CHAT = 1 + """:obj:`int`: The number of messages that can be sent per second in a particular chat. + Telegram may allow short bursts that go over this limit, but eventually you'll begin + receiving 429 errors. + """ + MESSAGES_PER_SECOND = 30 + """:obj:`int`: The number of messages that can roughly be sent in an interval of 30 seconds + across all chats. + """ + MESSAGES_PER_MINUTE_PER_GROUP = 20 + """:obj:`int`: The number of messages that can roughly be sent to a particular group within one + minute. + """ + + +class ForumIconColor(IntEnum): + """This enum contains the available colors for use in + :paramref:`telegram.Bot.create_forum_topic.icon_color`. The enum members of this enumeration + are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + BLUE = 0x6FB9F0 + """:obj:`int`: An icon with a color which corresponds to blue (``0x6FB9F0``). + + .. raw:: html + +
+ + """ + YELLOW = 0xFFD67E + """:obj:`int`: An icon with a color which corresponds to yellow (``0xFFD67E``). + + .. raw:: html + +
+ + """ + PURPLE = 0xCB86DB + """:obj:`int`: An icon with a color which corresponds to purple (``0xCB86DB``). + + .. raw:: html + +
+ + """ + GREEN = 0x8EEE98 + """:obj:`int`: An icon with a color which corresponds to green (``0x8EEE98``). + + .. raw:: html + +
+ + """ + PINK = 0xFF93B2 + """:obj:`int`: An icon with a color which corresponds to pink (``0xFF93B2``). + + .. raw:: html + +
+ + """ + RED = 0xFB6F5F + """:obj:`int`: An icon with a color which corresponds to red (``0xFB6F5F``). + + .. raw:: html + +
+ + """ + + +class GiveawayLimit(IntEnum): + """This enum contains limitations for :class:`telegram.Giveaway` and related classes. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.8 + """ + + __slots__ = () + + MAX_WINNERS = 100 + """:obj:`int`: Maximum number of winners allowed for :class:`telegram.GiveawayWinners.winners`. + """ + + +class KeyboardButtonRequestUsersLimit(IntEnum): + """This enum contains limitations for :class:`telegram.KeyboardButtonRequestUsers`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.8 + """ + + __slots__ = () + + MIN_QUANTITY = 1 + """:obj:`int`: Minimum value allowed for + :paramref:`~telegram.KeyboardButtonRequestUsers.max_quantity` parameter of + :class:`telegram.KeyboardButtonRequestUsers`. + """ + MAX_QUANTITY = 10 + """:obj:`int`: Maximum value allowed for + :paramref:`~telegram.KeyboardButtonRequestUsers.max_quantity` parameter of + :class:`telegram.KeyboardButtonRequestUsers`. + """ + + +class InlineKeyboardButtonLimit(IntEnum): + """This enum contains limitations for :class:`telegram.InlineKeyboardButton`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_CALLBACK_DATA = 1 + """:obj:`int`: Minimum value allowed for + :paramref:`~telegram.InlineKeyboardButton.callback_data` parameter of + :class:`telegram.InlineKeyboardButton` + """ + MAX_CALLBACK_DATA = 64 + """:obj:`int`: Maximum value allowed for + :paramref:`~telegram.InlineKeyboardButton.callback_data` parameter of + :class:`telegram.InlineKeyboardButton` + """ + + +class InlineKeyboardMarkupLimit(IntEnum): + """This enum contains limitations for :class:`telegram.InlineKeyboardMarkup`/ + :meth:`telegram.Bot.send_message` & friends. The enum + members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + TOTAL_BUTTON_NUMBER = 100 + """:obj:`int`: Maximum number of buttons that can be attached to a message. + + Note: + This value is undocumented and might be changed by Telegram. + """ + BUTTONS_PER_ROW = 8 + """:obj:`int`: Maximum number of buttons that can be attached to a message per row. + + Note: + This value is undocumented and might be changed by Telegram. + """ + + +class InputMediaType(StringEnum): + """This enum contains the available types of :class:`telegram.InputMedia`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + ANIMATION = "animation" + """:obj:`str`: Type of :class:`telegram.InputMediaAnimation`.""" + DOCUMENT = "document" + """:obj:`str`: Type of :class:`telegram.InputMediaDocument`.""" + AUDIO = "audio" + """:obj:`str`: Type of :class:`telegram.InputMediaAudio`.""" + PHOTO = "photo" + """:obj:`str`: Type of :class:`telegram.InputMediaPhoto`.""" + VIDEO = "video" + """:obj:`str`: Type of :class:`telegram.InputMediaVideo`.""" + + +class InputPaidMediaType(StringEnum): + """This enum contains the available types of :class:`telegram.InputPaidMedia`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 21.4 + """ + + __slots__ = () + + PHOTO = "photo" + """:obj:`str`: Type of :class:`telegram.InputMediaPhoto`.""" + VIDEO = "video" + """:obj:`str`: Type of :class:`telegram.InputMediaVideo`.""" + + +class InlineQueryLimit(IntEnum): + """This enum contains limitations for :class:`telegram.InlineQuery`/ + :meth:`telegram.Bot.answer_inline_query`. The enum members of this enumeration are instances + of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + RESULTS = 50 + """:obj:`int`: Maximum number of results that can be passed to + :meth:`telegram.Bot.answer_inline_query`.""" + MAX_OFFSET_LENGTH = 64 + """:obj:`int`: Maximum number of bytes in a :obj:`str` passed as the + :paramref:`~telegram.Bot.answer_inline_query.next_offset` parameter of + :meth:`telegram.Bot.answer_inline_query`.""" + MAX_QUERY_LENGTH = 256 + """:obj:`int`: Maximum number of characters in a :obj:`str` passed as the + :paramref:`~telegram.InlineQuery.query` parameter of :class:`telegram.InlineQuery`.""" + MIN_SWITCH_PM_TEXT_LENGTH = 1 + """:obj:`int`: Minimum number of characters in a :obj:`str` passed as the + :paramref:`~telegram.Bot.answer_inline_query.switch_pm_parameter` parameter of + :meth:`telegram.Bot.answer_inline_query`. + + .. deprecated:: 20.3 + Deprecated in favor of :attr:`InlineQueryResultsButtonLimit.MIN_START_PARAMETER_LENGTH`. + """ + MAX_SWITCH_PM_TEXT_LENGTH = 64 + """:obj:`int`: Maximum number of characters in a :obj:`str` passed as the + :paramref:`~telegram.Bot.answer_inline_query.switch_pm_parameter` parameter of + :meth:`telegram.Bot.answer_inline_query`. + + .. deprecated:: 20.3 + Deprecated in favor of :attr:`InlineQueryResultsButtonLimit.MAX_START_PARAMETER_LENGTH`. + """ + + +class InlineQueryResultLimit(IntEnum): + """This enum contains limitations for :class:`telegram.InlineQueryResult` and its subclasses. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_ID_LENGTH = 1 + """:obj:`int`: Minimum number of bytes in a :obj:`str` passed as the + :paramref:`~telegram.InlineQueryResult.id` parameter of + :class:`telegram.InlineQueryResult` and its subclasses + """ + MAX_ID_LENGTH = 64 + """:obj:`int`: Maximum number of bytes in a :obj:`str` passed as the + :paramref:`~telegram.InlineQueryResult.id` parameter of + :class:`telegram.InlineQueryResult` and its subclasses + """ + + +class InlineQueryResultsButtonLimit(IntEnum): + """This enum contains limitations for :class:`telegram.InlineQueryResultsButton`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.3 + """ + + __slots__ = () + + MIN_START_PARAMETER_LENGTH = InlineQueryLimit.MIN_SWITCH_PM_TEXT_LENGTH + """:obj:`int`: Minimum number of characters in a :obj:`str` passed as the + :paramref:`~telegram.InlineQueryResultsButton.start_parameter` parameter of + :meth:`telegram.InlineQueryResultsButton`.""" + + MAX_START_PARAMETER_LENGTH = InlineQueryLimit.MAX_SWITCH_PM_TEXT_LENGTH + """:obj:`int`: Maximum number of characters in a :obj:`str` passed as the + :paramref:`~telegram.InlineQueryResultsButton.start_parameter` parameter of + :meth:`telegram.InlineQueryResultsButton`.""" + + +class InlineQueryResultType(StringEnum): + """This enum contains the available types of :class:`telegram.InlineQueryResult`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + AUDIO = "audio" + """:obj:`str`: Type of :class:`telegram.InlineQueryResultAudio` and + :class:`telegram.InlineQueryResultCachedAudio`. + """ + DOCUMENT = "document" + """:obj:`str`: Type of :class:`telegram.InlineQueryResultDocument` and + :class:`telegram.InlineQueryResultCachedDocument`. + """ + GIF = "gif" + """:obj:`str`: Type of :class:`telegram.InlineQueryResultGif` and + :class:`telegram.InlineQueryResultCachedGif`. + """ + MPEG4GIF = "mpeg4_gif" + """:obj:`str`: Type of :class:`telegram.InlineQueryResultMpeg4Gif` and + :class:`telegram.InlineQueryResultCachedMpeg4Gif`. + """ + PHOTO = "photo" + """:obj:`str`: Type of :class:`telegram.InlineQueryResultPhoto` and + :class:`telegram.InlineQueryResultCachedPhoto`. + """ + STICKER = "sticker" + """:obj:`str`: Type of and :class:`telegram.InlineQueryResultCachedSticker`.""" + VIDEO = "video" + """:obj:`str`: Type of :class:`telegram.InlineQueryResultVideo` and + :class:`telegram.InlineQueryResultCachedVideo`. + """ + VOICE = "voice" + """:obj:`str`: Type of :class:`telegram.InlineQueryResultVoice` and + :class:`telegram.InlineQueryResultCachedVoice`. + """ + ARTICLE = "article" + """:obj:`str`: Type of :class:`telegram.InlineQueryResultArticle`.""" + CONTACT = "contact" + """:obj:`str`: Type of :class:`telegram.InlineQueryResultContact`.""" + GAME = "game" + """:obj:`str`: Type of :class:`telegram.InlineQueryResultGame`.""" + LOCATION = "location" + """:obj:`str`: Type of :class:`telegram.InlineQueryResultLocation`.""" + VENUE = "venue" + """:obj:`str`: Type of :class:`telegram.InlineQueryResultVenue`.""" + + +class LocationLimit(IntEnum): + """This enum contains limitations for + :class:`telegram.Location`/:class:`telegram.ChatLocation`/ + :meth:`telegram.Bot.edit_message_live_location`/:meth:`telegram.Bot.send_location`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_CHAT_LOCATION_ADDRESS = 1 + """:obj:`int`: Minimum value allowed for :paramref:`~telegram.ChatLocation.address` parameter + of :class:`telegram.ChatLocation` + """ + MAX_CHAT_LOCATION_ADDRESS = 64 + """:obj:`int`: Minimum value allowed for :paramref:`~telegram.ChatLocation.address` parameter + of :class:`telegram.ChatLocation` + """ + + HORIZONTAL_ACCURACY = 1500 + """:obj:`int`: Maximum value allowed for: + + * :paramref:`~telegram.Location.horizontal_accuracy` parameter of :class:`telegram.Location` + * :paramref:`~telegram.InlineQueryResultLocation.horizontal_accuracy` parameter of + :class:`telegram.InlineQueryResultLocation` + * :paramref:`~telegram.InputLocationMessageContent.horizontal_accuracy` parameter of + :class:`telegram.InputLocationMessageContent` + * :paramref:`~telegram.Bot.edit_message_live_location.horizontal_accuracy` parameter of + :meth:`telegram.Bot.edit_message_live_location` + * :paramref:`~telegram.Bot.send_location.horizontal_accuracy` parameter of + :meth:`telegram.Bot.send_location` + """ + + MIN_HEADING = 1 + """:obj:`int`: Minimum value allowed for: + + * :paramref:`~telegram.Location.heading` parameter of :class:`telegram.Location` + * :paramref:`~telegram.InlineQueryResultLocation.heading` parameter of + :class:`telegram.InlineQueryResultLocation` + * :paramref:`~telegram.InputLocationMessageContent.heading` parameter of + :class:`telegram.InputLocationMessageContent` + * :paramref:`~telegram.Bot.edit_message_live_location.heading` parameter of + :meth:`telegram.Bot.edit_message_live_location` + * :paramref:`~telegram.Bot.send_location.heading` parameter of + :meth:`telegram.Bot.send_location` + """ + MAX_HEADING = 360 + """:obj:`int`: Maximum value allowed for: + + * :paramref:`~telegram.Location.heading` parameter of :class:`telegram.Location` + * :paramref:`~telegram.InlineQueryResultLocation.heading` parameter of + :class:`telegram.InlineQueryResultLocation` + * :paramref:`~telegram.InputLocationMessageContent.heading` parameter of + :class:`telegram.InputLocationMessageContent` + * :paramref:`~telegram.Bot.edit_message_live_location.heading` parameter of + :meth:`telegram.Bot.edit_message_live_location` + * :paramref:`~telegram.Bot.send_location.heading` parameter of + :meth:`telegram.Bot.send_location` + """ + + MIN_LIVE_PERIOD = 60 + """:obj:`int`: Minimum value allowed for: + + * :paramref:`~telegram.InlineQueryResultLocation.live_period` parameter of + :class:`telegram.InlineQueryResultLocation` + * :paramref:`~telegram.InputLocationMessageContent.live_period` parameter of + :class:`telegram.InputLocationMessageContent` + * :paramref:`~telegram.Bot.edit_message_live_location.live_period` parameter of + :meth:`telegram.Bot.edit_message_live_location` + * :paramref:`~telegram.Bot.send_location.live_period` parameter of + :meth:`telegram.Bot.send_location` + """ + MAX_LIVE_PERIOD = 86400 + """:obj:`int`: Maximum value allowed for: + + * :paramref:`~telegram.InlineQueryResultLocation.live_period` parameter of + :class:`telegram.InlineQueryResultLocation` + * :paramref:`~telegram.InputLocationMessageContent.live_period` parameter of + :class:`telegram.InputLocationMessageContent` + * :paramref:`~telegram.Bot.edit_message_live_location.live_period` parameter of + :meth:`telegram.Bot.edit_message_live_location` + * :paramref:`~telegram.Bot.send_location.live_period` parameter of + :meth:`telegram.Bot.send_location` + """ + + LIVE_PERIOD_FOREVER = int(hex(0x7FFFFFFF), 16) + """:obj:`int`: Value for live locations that can be edited indefinitely. Passed in: + + * :paramref:`~telegram.InlineQueryResultLocation.live_period` parameter of + :class:`telegram.InlineQueryResultLocation` + * :paramref:`~telegram.InputLocationMessageContent.live_period` parameter of + :class:`telegram.InputLocationMessageContent` + * :paramref:`~telegram.Bot.edit_message_live_location.live_period` parameter of + :meth:`telegram.Bot.edit_message_live_location` + * :paramref:`~telegram.Bot.send_location.live_period` parameter of + :meth:`telegram.Bot.send_location` + + .. versionadded:: 21.2 + """ + + MIN_PROXIMITY_ALERT_RADIUS = 1 + """:obj:`int`: Minimum value allowed for: + + * :paramref:`~telegram.InlineQueryResultLocation.proximity_alert_radius` parameter of + :class:`telegram.InlineQueryResultLocation` + * :paramref:`~telegram.InputLocationMessageContent.proximity_alert_radius` parameter of + :class:`telegram.InputLocationMessageContent` + * :paramref:`~telegram.Bot.edit_message_live_location.proximity_alert_radius` parameter of + :meth:`telegram.Bot.edit_message_live_location` + * :paramref:`~telegram.Bot.send_location.proximity_alert_radius` parameter of + :meth:`telegram.Bot.send_location` + """ + MAX_PROXIMITY_ALERT_RADIUS = 100000 + """:obj:`int`: Maximum value allowed for: + + * :paramref:`~telegram.InlineQueryResultLocation.proximity_alert_radius` parameter of + :class:`telegram.InlineQueryResultLocation` + * :paramref:`~telegram.InputLocationMessageContent.proximity_alert_radius` parameter of + :class:`telegram.InputLocationMessageContent` + * :paramref:`~telegram.Bot.edit_message_live_location.proximity_alert_radius` parameter of + :meth:`telegram.Bot.edit_message_live_location` + * :paramref:`~telegram.Bot.send_location.proximity_alert_radius` parameter of + :meth:`telegram.Bot.send_location` + """ + + +class MaskPosition(StringEnum): + """This enum contains the available positions for :class:`telegram.MaskPosition`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + FOREHEAD = "forehead" + """:obj:`str`: Mask position for a sticker on the forehead.""" + EYES = "eyes" + """:obj:`str`: Mask position for a sticker on the eyes.""" + MOUTH = "mouth" + """:obj:`str`: Mask position for a sticker on the mouth.""" + CHIN = "chin" + """:obj:`str`: Mask position for a sticker on the chin.""" + + +class MediaGroupLimit(IntEnum): + """This enum contains limitations for :meth:`telegram.Bot.send_media_group`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_MEDIA_LENGTH = 2 + """:obj:`int`: Minimum length of a :obj:`list` passed as the + :paramref:`~telegram.Bot.send_media_group.media` parameter of + :meth:`telegram.Bot.send_media_group`. + """ + MAX_MEDIA_LENGTH = 10 + """:obj:`int`: Maximum length of a :obj:`list` passed as the + :paramref:`~telegram.Bot.send_media_group.media` parameter of + :meth:`telegram.Bot.send_media_group`. + """ + + +class MenuButtonType(StringEnum): + """This enum contains the available types of :class:`telegram.MenuButton`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + COMMANDS = "commands" + """:obj:`str`: The type of :class:`telegram.MenuButtonCommands`.""" + WEB_APP = "web_app" + """:obj:`str`: The type of :class:`telegram.MenuButtonWebApp`.""" + DEFAULT = "default" + """:obj:`str`: The type of :class:`telegram.MenuButtonDefault`.""" + + +class MessageAttachmentType(StringEnum): + """This enum contains the available types of :class:`telegram.Message` that can be seen + as attachment. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + # Make sure that all constants here are also listed in the MessageType Enum! + # (Enums are not extendable) + + ANIMATION = "animation" + """:obj:`str`: Messages with :attr:`telegram.Message.animation`.""" + AUDIO = "audio" + """:obj:`str`: Messages with :attr:`telegram.Message.audio`.""" + CONTACT = "contact" + """:obj:`str`: Messages with :attr:`telegram.Message.contact`.""" + DICE = "dice" + """:obj:`str`: Messages with :attr:`telegram.Message.dice`.""" + DOCUMENT = "document" + """:obj:`str`: Messages with :attr:`telegram.Message.document`.""" + GAME = "game" + """:obj:`str`: Messages with :attr:`telegram.Message.game`.""" + INVOICE = "invoice" + """:obj:`str`: Messages with :attr:`telegram.Message.invoice`.""" + LOCATION = "location" + """:obj:`str`: Messages with :attr:`telegram.Message.location`.""" + PAID_MEDIA = "paid_media" + """:obj:`str`: Messages with :attr:`telegram.Message.paid_media`. + + .. versionadded:: 21.4 + """ + PASSPORT_DATA = "passport_data" + """:obj:`str`: Messages with :attr:`telegram.Message.passport_data`.""" + PHOTO = "photo" + """:obj:`str`: Messages with :attr:`telegram.Message.photo`.""" + POLL = "poll" + """:obj:`str`: Messages with :attr:`telegram.Message.poll`.""" + STICKER = "sticker" + """:obj:`str`: Messages with :attr:`telegram.Message.sticker`.""" + STORY = "story" + """:obj:`str`: Messages with :attr:`telegram.Message.story`.""" + SUCCESSFUL_PAYMENT = "successful_payment" + """:obj:`str`: Messages with :attr:`telegram.Message.successful_payment`.""" + VIDEO = "video" + """:obj:`str`: Messages with :attr:`telegram.Message.video`.""" + VIDEO_NOTE = "video_note" + """:obj:`str`: Messages with :attr:`telegram.Message.video_note`.""" + VOICE = "voice" + """:obj:`str`: Messages with :attr:`telegram.Message.voice`.""" + VENUE = "venue" + """:obj:`str`: Messages with :attr:`telegram.Message.venue`.""" + + +class MessageEntityType(StringEnum): + """This enum contains the available types of :class:`telegram.MessageEntity`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + BLOCKQUOTE = "blockquote" + """:obj:`str`: Message entities representing a block quotation. + + .. versionadded:: 20.8 + """ + BOLD = "bold" + """:obj:`str`: Message entities representing bold text.""" + BOT_COMMAND = "bot_command" + """:obj:`str`: Message entities representing a bot command.""" + CASHTAG = "cashtag" + """:obj:`str`: Message entities representing a cashtag.""" + CODE = "code" + """:obj:`str`: Message entities representing monowidth string.""" + CUSTOM_EMOJI = "custom_emoji" + """:obj:`str`: Message entities representing inline custom emoji stickers. + + .. versionadded:: 20.0 + """ + EMAIL = "email" + """:obj:`str`: Message entities representing a email.""" + EXPANDABLE_BLOCKQUOTE = "expandable_blockquote" + """:obj:`str`: Message entities representing collapsed-by-default block quotation. + + .. versionadded:: 21.3 + """ + HASHTAG = "hashtag" + """:obj:`str`: Message entities representing a hashtag.""" + ITALIC = "italic" + """:obj:`str`: Message entities representing italic text.""" + MENTION = "mention" + """:obj:`str`: Message entities representing a mention.""" + PHONE_NUMBER = "phone_number" + """:obj:`str`: Message entities representing a phone number.""" + PRE = "pre" + """:obj:`str`: Message entities representing monowidth block.""" + SPOILER = "spoiler" + """:obj:`str`: Message entities representing spoiler text.""" + STRIKETHROUGH = "strikethrough" + """:obj:`str`: Message entities representing strikethrough text.""" + TEXT_LINK = "text_link" + """:obj:`str`: Message entities representing clickable text URLs.""" + TEXT_MENTION = "text_mention" + """:obj:`str`: Message entities representing text mention for users without usernames.""" + UNDERLINE = "underline" + """:obj:`str`: Message entities representing underline text.""" + URL = "url" + """:obj:`str`: Message entities representing a url.""" + + +class MessageLimit(IntEnum): + """This enum contains limitations for :class:`telegram.Message`/ + :class:`telegram.InputTextMessageContent`/ + :meth:`telegram.Bot.send_message` & friends. The enum + members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + # TODO add links to params? + MAX_TEXT_LENGTH = 4096 + """:obj:`int`: Maximum number of characters in a :obj:`str` passed as: + + * :paramref:`~telegram.Game.text` parameter of :class:`telegram.Game` + * :paramref:`~telegram.Message.text` parameter of :class:`telegram.Message` + * :paramref:`~telegram.InputTextMessageContent.message_text` parameter of + :class:`telegram.InputTextMessageContent` + * :paramref:`~telegram.Bot.send_message.text` parameter of :meth:`telegram.Bot.send_message` + * :paramref:`~telegram.Bot.edit_message_text.text` parameter of + :meth:`telegram.Bot.edit_message_text` + """ + CAPTION_LENGTH = 1024 + """:obj:`int`: Maximum number of characters in a :obj:`str` passed as: + + * :paramref:`~telegram.Message.caption` parameter of :class:`telegram.Message` + * :paramref:`~telegram.InputMedia.caption` parameter of :class:`telegram.InputMedia` + and its subclasses + * ``caption`` parameter of subclasses of :class:`telegram.InlineQueryResult` + * ``caption`` parameter of :meth:`telegram.Bot.send_photo`, :meth:`telegram.Bot.send_audio`, + :meth:`telegram.Bot.send_document`, :meth:`telegram.Bot.send_video`, + :meth:`telegram.Bot.send_animation`, :meth:`telegram.Bot.send_voice`, + :meth:`telegram.Bot.edit_message_caption`, :meth:`telegram.Bot.copy_message` + """ + # constants above this line are tested + MIN_TEXT_LENGTH = 1 + """:obj:`int`: Minimum number of characters in a :obj:`str` passed as the + :paramref:`~telegram.InputTextMessageContent.message_text` parameter of + :class:`telegram.InputTextMessageContent` and the + :paramref:`~telegram.Bot.edit_message_text.text` parameter of + :meth:`telegram.Bot.edit_message_text`. + """ + # TODO this constant is not used. helpers.py contains 64 as a number + DEEP_LINK_LENGTH = 64 + """:obj:`int`: Maximum number of characters for a deep link.""" + # TODO this constant is not used anywhere + MESSAGE_ENTITIES = 100 + """:obj:`int`: Maximum number of entities that can be displayed in a message. Further entities + will simply be ignored by Telegram. + + Note: + This value is undocumented and might be changed by Telegram. + """ + + +class MessageOriginType(StringEnum): + """This enum contains the available types of :class:`telegram.MessageOrigin`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.8 + """ + + __slots__ = () + + USER = "user" + """:obj:`str`: A :class:`telegram.MessageOrigin` who is sent by an user.""" + HIDDEN_USER = "hidden_user" + """:obj:`str`: A :class:`telegram.MessageOrigin` who is sent by a hidden user.""" + CHAT = "chat" + """:obj:`str`: A :class:`telegram.MessageOrigin` who is sent by a chat.""" + CHANNEL = "channel" + """:obj:`str`: A :class:`telegram.MessageOrigin` who is sent by a channel.""" + + +class MessageType(StringEnum): + """This enum contains the available types of :class:`telegram.Message`. Here, a "type" means + a kind of message that is visually distinct from other kinds of messages in the Telegram app. + In particular, auxiliary attributes that can be present for multiple types of messages are + not considered in this enumeration. + + The enum members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + # Make sure that all attachment type constants are also listed in the + # MessageAttachmentType Enum! (Enums are not extendable) + + ANIMATION = "animation" + """:obj:`str`: Messages with :attr:`telegram.Message.animation`.""" + AUDIO = "audio" + """:obj:`str`: Messages with :attr:`telegram.Message.audio`.""" + BOOST_ADDED = "boost_added" + """:obj:`str`: Messages with :attr:`telegram.Message.boost_added`. + + .. versionadded:: 21.0 + """ + BUSINESS_CONNECTION_ID = "business_connection_id" + """:obj:`str`: Messages with :attr:`telegram.Message.business_connection_id`. + + .. versionadded:: 21.1 + """ + CHANNEL_CHAT_CREATED = "channel_chat_created" + """:obj:`str`: Messages with :attr:`telegram.Message.channel_chat_created`.""" + CHAT_SHARED = "chat_shared" + """:obj:`str`: Messages with :attr:`telegram.Message.chat_shared`. + + .. versionadded:: 20.8 + """ + CHAT_BACKGROUND_SET = "chat_background_set" + """:obj:`str`: Messages with :attr:`telegram.Message.chat_background_set`. + + .. versionadded:: 21.2 + """ + CONNECTED_WEBSITE = "connected_website" + """:obj:`str`: Messages with :attr:`telegram.Message.connected_website`.""" + CONTACT = "contact" + """:obj:`str`: Messages with :attr:`telegram.Message.contact`.""" + DELETE_CHAT_PHOTO = "delete_chat_photo" + """:obj:`str`: Messages with :attr:`telegram.Message.delete_chat_photo`.""" + DICE = "dice" + """:obj:`str`: Messages with :attr:`telegram.Message.dice`.""" + DOCUMENT = "document" + """:obj:`str`: Messages with :attr:`telegram.Message.document`.""" + EFFECT_ID = "effect_id" + """:obj:`str`: Messages with :attr:`telegram.Message.effect_id`. + + .. versionadded:: 21.3""" + FORUM_TOPIC_CREATED = "forum_topic_created" + """:obj:`str`: Messages with :attr:`telegram.Message.forum_topic_created`. + + .. versionadded:: 20.8 + """ + FORUM_TOPIC_CLOSED = "forum_topic_closed" + """:obj:`str`: Messages with :attr:`telegram.Message.forum_topic_closed`. + + .. versionadded:: 20.8 + """ + FORUM_TOPIC_EDITED = "forum_topic_edited" + """:obj:`str`: Messages with :attr:`telegram.Message.forum_topic_edited`. + + .. versionadded:: 20.8 + """ + FORUM_TOPIC_REOPENED = "forum_topic_reopened" + """:obj:`str`: Messages with :attr:`telegram.Message.forum_topic_reopened`. + + .. versionadded:: 20.8 + """ + GAME = "game" + """:obj:`str`: Messages with :attr:`telegram.Message.game`.""" + GENERAL_FORUM_TOPIC_HIDDEN = "general_forum_topic_hidden" + """:obj:`str`: Messages with :attr:`telegram.Message.general_forum_topic_hidden`. + + .. versionadded:: 20.8 + """ + GENERAL_FORUM_TOPIC_UNHIDDEN = "general_forum_topic_unhidden" + """:obj:`str`: Messages with :attr:`telegram.Message.general_forum_topic_unhidden`. + + .. versionadded:: 20.8 + """ + GIVEAWAY = "giveaway" + """:obj:`str`: Messages with :attr:`telegram.Message.giveaway`. + + .. versionadded:: 20.8 + """ + GIVEAWAY_CREATED = "giveaway_created" + """:obj:`str`: Messages with :attr:`telegram.Message.giveaway_created`. + + .. versionadded:: 20.8 + """ + GIVEAWAY_WINNERS = "giveaway_winners" + """:obj:`str`: Messages with :attr:`telegram.Message.giveaway_winners`. + + .. versionadded:: 20.8 + """ + GIVEAWAY_COMPLETED = "giveaway_completed" + """:obj:`str`: Messages with :attr:`telegram.Message.giveaway_completed`. + + .. versionadded:: 20.8 + """ + GROUP_CHAT_CREATED = "group_chat_created" + """:obj:`str`: Messages with :attr:`telegram.Message.group_chat_created`.""" + INVOICE = "invoice" + """:obj:`str`: Messages with :attr:`telegram.Message.invoice`.""" + LEFT_CHAT_MEMBER = "left_chat_member" + """:obj:`str`: Messages with :attr:`telegram.Message.left_chat_member`.""" + LOCATION = "location" + """:obj:`str`: Messages with :attr:`telegram.Message.location`.""" + MESSAGE_AUTO_DELETE_TIMER_CHANGED = "message_auto_delete_timer_changed" + """:obj:`str`: Messages with :attr:`telegram.Message.message_auto_delete_timer_changed`.""" + MIGRATE_TO_CHAT_ID = "migrate_to_chat_id" + """:obj:`str`: Messages with :attr:`telegram.Message.migrate_to_chat_id`.""" + NEW_CHAT_MEMBERS = "new_chat_members" + """:obj:`str`: Messages with :attr:`telegram.Message.new_chat_members`.""" + NEW_CHAT_TITLE = "new_chat_title" + """:obj:`str`: Messages with :attr:`telegram.Message.new_chat_title`.""" + NEW_CHAT_PHOTO = "new_chat_photo" + """:obj:`str`: Messages with :attr:`telegram.Message.new_chat_photo`.""" + PAID_MEDIA = "paid_media" + """:obj:`str`: Messages with :attr:`telegram.Message.paid_media`. + + .. versionadded:: 21.4 + """ + PASSPORT_DATA = "passport_data" + """:obj:`str`: Messages with :attr:`telegram.Message.passport_data`.""" + PHOTO = "photo" + """:obj:`str`: Messages with :attr:`telegram.Message.photo`.""" + PINNED_MESSAGE = "pinned_message" + """:obj:`str`: Messages with :attr:`telegram.Message.pinned_message`.""" + POLL = "poll" + """:obj:`str`: Messages with :attr:`telegram.Message.poll`.""" + PROXIMITY_ALERT_TRIGGERED = "proximity_alert_triggered" + """:obj:`str`: Messages with :attr:`telegram.Message.proximity_alert_triggered`.""" + REFUNDED_PAYMENT = "refunded_payment" + """:obj:`str`: Messages with :attr:`telegram.Message.refunded_payment`. + + .. versionadded:: 21.4 + """ + REPLY_TO_STORY = "reply_to_story" + """:obj:`str`: Messages with :attr:`telegram.Message.reply_to_story`. + + .. versionadded:: 21.0 + """ + SENDER_BOOST_COUNT = "sender_boost_count" + """:obj:`str`: Messages with :attr:`telegram.Message.sender_boost_count`. + + .. versionadded:: 21.0 + """ + SENDER_BUSINESS_BOT = "sender_business_bot" + """:obj:`str`: Messages with :attr:`telegram.Message.sender_business_bot`. + + .. versionadded:: 21.1 + """ + STICKER = "sticker" + """:obj:`str`: Messages with :attr:`telegram.Message.sticker`.""" + STORY = "story" + """:obj:`str`: Messages with :attr:`telegram.Message.story`.""" + SUPERGROUP_CHAT_CREATED = "supergroup_chat_created" + """:obj:`str`: Messages with :attr:`telegram.Message.supergroup_chat_created`.""" + SUCCESSFUL_PAYMENT = "successful_payment" + """:obj:`str`: Messages with :attr:`telegram.Message.successful_payment`.""" + TEXT = "text" + """:obj:`str`: Messages with :attr:`telegram.Message.text`.""" + USERS_SHARED = "users_shared" + """:obj:`str`: Messages with :attr:`telegram.Message.users_shared`. + + .. versionadded:: 20.8 + """ + VENUE = "venue" + """:obj:`str`: Messages with :attr:`telegram.Message.venue`.""" + VIDEO = "video" + """:obj:`str`: Messages with :attr:`telegram.Message.video`.""" + VIDEO_CHAT_SCHEDULED = "video_chat_scheduled" + """:obj:`str`: Messages with :attr:`telegram.Message.video_chat_scheduled`.""" + VIDEO_CHAT_STARTED = "video_chat_started" + """:obj:`str`: Messages with :attr:`telegram.Message.video_chat_started`.""" + VIDEO_CHAT_ENDED = "video_chat_ended" + """:obj:`str`: Messages with :attr:`telegram.Message.video_chat_ended`.""" + VIDEO_CHAT_PARTICIPANTS_INVITED = "video_chat_participants_invited" + """:obj:`str`: Messages with :attr:`telegram.Message.video_chat_participants_invited`.""" + VIDEO_NOTE = "video_note" + """:obj:`str`: Messages with :attr:`telegram.Message.video_note`.""" + VOICE = "voice" + """:obj:`str`: Messages with :attr:`telegram.Message.voice`.""" + WEB_APP_DATA = "web_app_data" + """:obj:`str`: Messages with :attr:`telegram.Message.web_app_data`. + + .. versionadded:: 20.8 + """ + WRITE_ACCESS_ALLOWED = "write_access_allowed" + """:obj:`str`: Messages with :attr:`telegram.Message.write_access_allowed`. + + .. versionadded:: 20.8 + """ + + +class PaidMediaType(StringEnum): + """ + This enum contains the available types of :class:`telegram.PaidMedia`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 21.4 + """ + + __slots__ = () + + PREVIEW = "preview" + """:obj:`str`: The type of :class:`telegram.PaidMediaPreview`.""" + VIDEO = "video" + """:obj:`str`: The type of :class:`telegram.PaidMediaVideo`.""" + PHOTO = "photo" + """:obj:`str`: The type of :class:`telegram.PaidMediaPhoto`.""" + + +class PollingLimit(IntEnum): + """This enum contains limitations for :paramref:`telegram.Bot.get_updates.limit`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_LIMIT = 1 + """:obj:`int`: Minimum value allowed for the :paramref:`~telegram.Bot.get_updates.limit` + parameter of :meth:`telegram.Bot.get_updates`. + """ + MAX_LIMIT = 100 + """:obj:`int`: Maximum value allowed for the :paramref:`~telegram.Bot.get_updates.limit` + parameter of :meth:`telegram.Bot.get_updates`. + """ + + +class ProfileAccentColor(Enum): + """This enum contains the available accent colors for + :class:`telegram.ChatFullInfo.profile_accent_color_id`. + The members of this enum are named tuples with the following attributes: + + - ``identifier`` (:obj:`int`): The identifier of the accent color. + - ``name`` (:obj:`str`): Optional. The name of the accent color. + - ``light_colors`` (Tuple[:obj:`str`]): Optional. The light colors of the accent color as HEX + value. + - ``dark_colors`` (Tuple[:obj:`str`]): Optional. The dark colors of the accent color as HEX + value. + + Since Telegram gives no exact specification for the accent colors, future accent colors might + have a different data type. + + .. versionadded:: 20.8 + """ + + __slots__ = () + + COLOR_000 = _AccentColor(identifier=0, light_colors=(0xBA5650,), dark_colors=(0x9C4540,)) + """Accent color 0. This contains one light color + + .. raw:: html + +
+
+ + and one dark color + + .. raw:: html + +
+
+ """ + COLOR_001 = _AccentColor(identifier=1, light_colors=(0xC27C3E,), dark_colors=(0x945E2C,)) + """Accent color 1. This contains one light color + + .. raw:: html + +
+
+ + and one dark color + + .. raw:: html + +
+
+ """ + COLOR_002 = _AccentColor(identifier=2, light_colors=(0x956AC8,), dark_colors=(0x715099,)) + """Accent color 2. This contains one light color + + .. raw:: html + +
+
+ + and one dark color + + .. raw:: html + +
+
+ """ + COLOR_003 = _AccentColor(identifier=3, light_colors=(0x49A355,), dark_colors=(0x33713B,)) + """Accent color 3. This contains one light color + + .. raw:: html + +
+
+ + and one dark color + + .. raw:: html + +
+
+ """ + COLOR_004 = _AccentColor(identifier=4, light_colors=(0x3E97AD,), dark_colors=(0x387E87,)) + """Accent color 4. This contains one light color + + .. raw:: html + +
+
+ + and one dark color + + .. raw:: html + +
+
+ """ + COLOR_005 = _AccentColor(identifier=5, light_colors=(0x5A8FBB,), dark_colors=(0x477194,)) + """Accent color 5. This contains one light color + + .. raw:: html + +
+
+ + and one dark color + + .. raw:: html + +
+
+ """ + COLOR_006 = _AccentColor(identifier=6, light_colors=(0xB85378,), dark_colors=(0x944763,)) + """Accent color 6. This contains one light color + + .. raw:: html + +
+
+ + and one dark color + + .. raw:: html + +
+
+ """ + COLOR_007 = _AccentColor(identifier=7, light_colors=(0x7F8B95,), dark_colors=(0x435261,)) + """Accent color 7. This contains one light color + + .. raw:: html + +
+
+ + and one dark color + + .. raw:: html + +
+
+ """ + COLOR_008 = _AccentColor( + identifier=8, light_colors=(0xC9565D, 0xD97C57), dark_colors=(0x994343, 0xAC583E) + ) + """Accent color 8. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + COLOR_009 = _AccentColor( + identifier=9, light_colors=(0xCF7244, 0xCC9433), dark_colors=(0x8F552F, 0xA17232) + ) + """Accent color 9. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + COLOR_010 = _AccentColor( + identifier=10, light_colors=(0x9662D4, 0xB966B6), dark_colors=(0x634691, 0x9250A2) + ) + """Accent color 10. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + COLOR_011 = _AccentColor( + identifier=11, light_colors=(0x3D9755, 0x89A650), dark_colors=(0x296A43, 0x5F8F44) + ) + """Accent color 11. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + COLOR_012 = _AccentColor( + identifier=12, light_colors=(0x3D95BA, 0x50AD98), dark_colors=(0x306C7C, 0x3E987E) + ) + """Accent color 12. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + COLOR_013 = _AccentColor( + identifier=13, light_colors=(0x538BC2, 0x4DA8BD), dark_colors=(0x38618C, 0x458BA1) + ) + """Accent color 13. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + COLOR_014 = _AccentColor( + identifier=14, light_colors=(0xB04F74, 0xD1666D), dark_colors=(0x884160, 0xA65259) + ) + """Accent color 14. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + COLOR_015 = _AccentColor( + identifier=15, light_colors=(0x637482, 0x7B8A97), dark_colors=(0x53606E, 0x384654) + ) + """Accent color 15. This contains two light colors + + .. raw:: html + +
+
+
+

+ + and two dark colors + + .. raw:: html + +
+
+
+

+ """ + + +class ReplyLimit(IntEnum): + """This enum contains limitations for :class:`telegram.ForceReply` + and :class:`telegram.ReplyKeyboardMarkup`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_INPUT_FIELD_PLACEHOLDER = 1 + """:obj:`int`: Minimum value allowed for + :paramref:`~telegram.ForceReply.input_field_placeholder` parameter of + :class:`telegram.ForceReply` and + :paramref:`~telegram.ReplyKeyboardMarkup.input_field_placeholder` parameter of + :class:`telegram.ReplyKeyboardMarkup` + """ + MAX_INPUT_FIELD_PLACEHOLDER = 64 + """:obj:`int`: Maximum value allowed for + :paramref:`~telegram.ForceReply.input_field_placeholder` parameter of + :class:`telegram.ForceReply` and + :paramref:`~telegram.ReplyKeyboardMarkup.input_field_placeholder` parameter of + :class:`telegram.ReplyKeyboardMarkup` + """ + + +class RevenueWithdrawalStateType(StringEnum): + """This enum contains the available types of :class:`telegram.RevenueWithdrawalState`. + The enum members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 21.4 + """ + + __slots__ = () + + PENDING = "pending" + """:obj:`str`: A withdrawal in progress.""" + SUCCEEDED = "succeeded" + """:obj:`str`: A withdrawal succeeded.""" + FAILED = "failed" + """:obj:`str`: A withdrawal failed and the transaction was refunded.""" + + +class StarTransactionsLimit(IntEnum): + """This enum contains limitations for :class:`telegram.Bot.get_star_transactions`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 21.4 + """ + + __slots__ = () + + MIN_LIMIT = 1 + """:obj:`int`: Minimum value allowed for the + :paramref:`~telegram.Bot.get_star_transactions.limit` parameter of + :meth:`telegram.Bot.get_star_transactions`.""" + MAX_LIMIT = 100 + """:obj:`int`: Maximum value allowed for the + :paramref:`~telegram.Bot.get_star_transactions.limit` parameter of + :meth:`telegram.Bot.get_star_transactions`.""" + + +class StickerFormat(StringEnum): + """This enum contains the available formats of :class:`telegram.Sticker` in the set. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.2 + """ + + __slots__ = () + + STATIC = "static" + """:obj:`str`: Static sticker.""" + ANIMATED = "animated" + """:obj:`str`: Animated sticker.""" + VIDEO = "video" + """:obj:`str`: Video sticker.""" + + +class StickerLimit(IntEnum): + """This enum contains limitations for various sticker methods, such as + :meth:`telegram.Bot.create_new_sticker_set`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_NAME_AND_TITLE = 1 + """:obj:`int`: Minimum number of characters in a :obj:`str` passed as the + :paramref:`~telegram.Bot.create_new_sticker_set.name` parameter or the + :paramref:`~telegram.Bot.create_new_sticker_set.title` parameter of + :meth:`telegram.Bot.create_new_sticker_set`. + """ + MAX_NAME_AND_TITLE = 64 + """:obj:`int`: Maximum number of characters in a :obj:`str` passed as the + :paramref:`~telegram.Bot.create_new_sticker_set.name` parameter or the + :paramref:`~telegram.Bot.create_new_sticker_set.title` parameter of + :meth:`telegram.Bot.create_new_sticker_set`. + """ + MIN_STICKER_EMOJI = 1 + """:obj:`int`: Minimum number of emojis associated with a sticker, passed as the + :paramref:`~telegram.Bot.setStickerEmojiList.emoji_list` parameter of + :meth:`telegram.Bot.set_sticker_emoji_list`. + + .. versionadded:: 20.2 + """ + MAX_STICKER_EMOJI = 20 + """:obj:`int`: Maximum number of emojis associated with a sticker, passed as the + :paramref:`~telegram.Bot.setStickerEmojiList.emoji_list` parameter of + :meth:`telegram.Bot.set_sticker_emoji_list`. + + .. versionadded:: 20.2 + """ + MAX_SEARCH_KEYWORDS = 20 + """:obj:`int`: Maximum number of search keywords for a sticker, passed as the + :paramref:`~telegram.Bot.set_sticker_keywords.keywords` parameter of + :meth:`telegram.Bot.set_sticker_keywords`. + + .. versionadded:: 20.2 + """ + MAX_KEYWORD_LENGTH = 64 + """:obj:`int`: Maximum number of characters in a search keyword for a sticker, for each item in + :paramref:`~telegram.Bot.set_sticker_keywords.keywords` sequence of + :meth:`telegram.Bot.set_sticker_keywords`. + + .. versionadded:: 20.2 + """ + + +class StickerSetLimit(IntEnum): + """This enum contains limitations for various sticker set methods, such as + :meth:`telegram.Bot.create_new_sticker_set` and :meth:`telegram.Bot.add_sticker_to_set`. + + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.2 + """ + + __slots__ = () + + MIN_INITIAL_STICKERS = 1 + """:obj:`int`: Minimum number of stickers needed to create a sticker set, passed as the + :paramref:`~telegram.Bot.create_new_sticker_set.stickers` parameter of + :meth:`telegram.Bot.create_new_sticker_set`. + """ + MAX_INITIAL_STICKERS = 50 + """:obj:`int`: Maximum number of stickers allowed while creating a sticker set, passed as the + :paramref:`~telegram.Bot.create_new_sticker_set.stickers` parameter of + :meth:`telegram.Bot.create_new_sticker_set`. + """ + MAX_EMOJI_STICKERS = 200 + """:obj:`int`: Maximum number of stickers allowed in an emoji sticker set, as given in + :meth:`telegram.Bot.add_sticker_to_set`. + """ + MAX_ANIMATED_STICKERS = 50 + """:obj:`int`: Maximum number of stickers allowed in an animated or video sticker set, as given + in :meth:`telegram.Bot.add_sticker_to_set`. + + .. deprecated:: 21.1 + The animated sticker limit is now 120, the same as :attr:`MAX_STATIC_STICKERS`. + """ + MAX_STATIC_STICKERS = 120 + """:obj:`int`: Maximum number of stickers allowed in a static sticker set, as given in + :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, + 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, + 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 + pixels, as given in :meth:`telegram.Bot.set_sticker_set_thumbnail`.""" + + +class StickerType(StringEnum): + """This enum contains the available types of :class:`telegram.Sticker`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + REGULAR = "regular" + """:obj:`str`: Regular sticker.""" + MASK = "mask" + """:obj:`str`: Mask sticker.""" + CUSTOM_EMOJI = "custom_emoji" + """:obj:`str`: Custom emoji sticker.""" + + +class TransactionPartnerType(StringEnum): + """This enum contains the available types of :class:`telegram.TransactionPartner`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 21.4 + """ + + __slots__ = () + + FRAGMENT = "fragment" + """:obj:`str`: Withdrawal transaction with Fragment.""" + USER = "user" + """:obj:`str`: Transaction with a user.""" + OTHER = "other" + """:obj:`str`: Transaction with unknown source or recipient.""" + TELEGRAM_ADS = "telegram_ads" + """:obj:`str`: Transaction with Telegram Ads.""" + + +class ParseMode(StringEnum): + """This enum contains the available parse modes. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MARKDOWN = "Markdown" + """:obj:`str`: Markdown parse mode. + + Note: + :attr:`MARKDOWN` is a legacy mode, retained by Telegram for backward compatibility. + You should use :attr:`MARKDOWN_V2` instead. + """ + MARKDOWN_V2 = "MarkdownV2" + """:obj:`str`: Markdown parse mode version 2.""" + HTML = "HTML" + """:obj:`str`: HTML parse mode.""" + + +class PollLimit(IntEnum): + """This enum contains limitations for :class:`telegram.Poll`/:class:`telegram.PollOption`/ + :meth:`telegram.Bot.send_poll`. The enum + members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_QUESTION_LENGTH = 1 + """:obj:`int`: Minimum value allowed for the :paramref:`~telegram.Poll.question` + parameter of :class:`telegram.Poll` and the :paramref:`~telegram.Bot.send_poll.question` + parameter of :meth:`telegram.Bot.send_poll`. + """ + MAX_QUESTION_LENGTH = 300 + """:obj:`int`: Maximum value allowed for the :paramref:`~telegram.Poll.question` + parameter of :class:`telegram.Poll` and the :paramref:`~telegram.Bot.send_poll.question` + parameter of :meth:`telegram.Bot.send_poll`. + """ + MIN_OPTION_LENGTH = 1 + """:obj:`int`: Minimum length of each :obj:`str` passed in a :obj:`list` + to the :paramref:`~telegram.Bot.send_poll.options` parameter of + :meth:`telegram.Bot.send_poll`. + """ + MAX_OPTION_LENGTH = 100 + """:obj:`int`: Maximum length of each :obj:`str` passed in a :obj:`list` + to the :paramref:`~telegram.Bot.send_poll.options` parameter of + :meth:`telegram.Bot.send_poll`. + """ + MIN_OPTION_NUMBER = 2 + """:obj:`int`: Minimum number of strings passed in a :obj:`list` + to the :paramref:`~telegram.Bot.send_poll.options` parameter of + :meth:`telegram.Bot.send_poll`. + """ + MAX_OPTION_NUMBER = 10 + """:obj:`int`: Maximum number of strings passed in a :obj:`list` + to the :paramref:`~telegram.Bot.send_poll.options` parameter of + :meth:`telegram.Bot.send_poll`. + """ + MAX_EXPLANATION_LENGTH = 200 + """:obj:`int`: Maximum number of characters in a :obj:`str` passed as the + :paramref:`~telegram.Poll.explanation` parameter of :class:`telegram.Poll` and the + :paramref:`~telegram.Bot.send_poll.explanation` parameter of :meth:`telegram.Bot.send_poll`. + """ + MAX_EXPLANATION_LINE_FEEDS = 2 + """:obj:`int`: Maximum number of line feeds in a :obj:`str` passed as the + :paramref:`~telegram.Bot.send_poll.explanation` parameter of :meth:`telegram.Bot.send_poll` + after entities parsing. + """ + MIN_OPEN_PERIOD = 5 + """:obj:`int`: Minimum value allowed for the + :paramref:`~telegram.Bot.send_poll.open_period` parameter of :meth:`telegram.Bot.send_poll`. + Also used in the :paramref:`~telegram.Bot.send_poll.close_date` parameter of + :meth:`telegram.Bot.send_poll`. + """ + MAX_OPEN_PERIOD = 600 + """:obj:`int`: Maximum value allowed for the + :paramref:`~telegram.Bot.send_poll.open_period` parameter of :meth:`telegram.Bot.send_poll`. + Also used in the :paramref:`~telegram.Bot.send_poll.close_date` parameter of + :meth:`telegram.Bot.send_poll`. + """ + + +class PollType(StringEnum): + """This enum contains the available types for :class:`telegram.Poll`/ + :meth:`telegram.Bot.send_poll`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + REGULAR = "regular" + """:obj:`str`: regular polls.""" + QUIZ = "quiz" + """:obj:`str`: quiz polls.""" + + +class UpdateType(StringEnum): + """This enum contains the available types of :class:`telegram.Update`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MESSAGE = "message" + """:obj:`str`: Updates with :attr:`telegram.Update.message`.""" + EDITED_MESSAGE = "edited_message" + """:obj:`str`: Updates with :attr:`telegram.Update.edited_message`.""" + CHANNEL_POST = "channel_post" + """:obj:`str`: Updates with :attr:`telegram.Update.channel_post`.""" + EDITED_CHANNEL_POST = "edited_channel_post" + """:obj:`str`: Updates with :attr:`telegram.Update.edited_channel_post`.""" + INLINE_QUERY = "inline_query" + """:obj:`str`: Updates with :attr:`telegram.Update.inline_query`.""" + CHOSEN_INLINE_RESULT = "chosen_inline_result" + """:obj:`str`: Updates with :attr:`telegram.Update.chosen_inline_result`.""" + CALLBACK_QUERY = "callback_query" + """:obj:`str`: Updates with :attr:`telegram.Update.callback_query`.""" + SHIPPING_QUERY = "shipping_query" + """:obj:`str`: Updates with :attr:`telegram.Update.shipping_query`.""" + PRE_CHECKOUT_QUERY = "pre_checkout_query" + """:obj:`str`: Updates with :attr:`telegram.Update.pre_checkout_query`.""" + POLL = "poll" + """:obj:`str`: Updates with :attr:`telegram.Update.poll`.""" + POLL_ANSWER = "poll_answer" + """:obj:`str`: Updates with :attr:`telegram.Update.poll_answer`.""" + MY_CHAT_MEMBER = "my_chat_member" + """:obj:`str`: Updates with :attr:`telegram.Update.my_chat_member`.""" + CHAT_MEMBER = "chat_member" + """:obj:`str`: Updates with :attr:`telegram.Update.chat_member`.""" + CHAT_JOIN_REQUEST = "chat_join_request" + """:obj:`str`: Updates with :attr:`telegram.Update.chat_join_request`.""" + CHAT_BOOST = "chat_boost" + """:obj:`str`: Updates with :attr:`telegram.Update.chat_boost`. + + .. versionadded:: 20.8 + """ + REMOVED_CHAT_BOOST = "removed_chat_boost" + """:obj:`str`: Updates with :attr:`telegram.Update.removed_chat_boost`. + + .. versionadded:: 20.8 + """ + MESSAGE_REACTION = "message_reaction" + """:obj:`str`: Updates with :attr:`telegram.Update.message_reaction`. + + .. versionadded:: 20.8 + """ + MESSAGE_REACTION_COUNT = "message_reaction_count" + """:obj:`str`: Updates with :attr:`telegram.Update.message_reaction_count`. + + .. versionadded:: 20.8 + """ + BUSINESS_CONNECTION = "business_connection" + """:obj:`str`: Updates with :attr:`telegram.Update.business_connection`. + + .. versionadded:: 21.1 + """ + BUSINESS_MESSAGE = "business_message" + """:obj:`str`: Updates with :attr:`telegram.Update.business_message`. + + .. versionadded:: 21.1 + """ + EDITED_BUSINESS_MESSAGE = "edited_business_message" + """:obj:`str`: Updates with :attr:`telegram.Update.edited_business_message`. + + .. versionadded:: 21.1 + """ + DELETED_BUSINESS_MESSAGES = "deleted_business_messages" + """:obj:`str`: Updates with :attr:`telegram.Update.deleted_business_messages`. + + .. versionadded:: 21.1 + """ + + +class InvoiceLimit(IntEnum): + """This enum contains limitations for :class:`telegram.InputInvoiceMessageContent`, + :meth:`telegram.Bot.send_invoice`, and :meth:`telegram.Bot.create_invoice_link`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_TITLE_LENGTH = 1 + """:obj:`int`: Minimum number of characters in a :obj:`str` passed as: + + * :paramref:`~telegram.InputInvoiceMessageContent.title` parameter of + :class:`telegram.InputInvoiceMessageContent` + * :paramref:`~telegram.Bot.send_invoice.title` parameter of + :meth:`telegram.Bot.send_invoice`. + * :paramref:`~telegram.Bot.create_invoice_link.title` parameter of + :meth:`telegram.Bot.create_invoice_link`. + """ + MAX_TITLE_LENGTH = 32 + """:obj:`int`: Maximum number of characters in a :obj:`str` passed as: + + * :paramref:`~telegram.InputInvoiceMessageContent.title` parameter of + :class:`telegram.InputInvoiceMessageContent` + * :paramref:`~telegram.Bot.send_invoice.title` parameter of + :meth:`telegram.Bot.send_invoice`. + * :paramref:`~telegram.Bot.create_invoice_link.title` parameter of + :meth:`telegram.Bot.create_invoice_link`. + """ + MIN_DESCRIPTION_LENGTH = 1 + """:obj:`int`: Minimum number of characters in a :obj:`str` passed as: + + * :paramref:`~telegram.InputInvoiceMessageContent.description` parameter of + :class:`telegram.InputInvoiceMessageContent` + * :paramref:`~telegram.Bot.send_invoice.description` parameter of + :meth:`telegram.Bot.send_invoice`. + * :paramref:`~telegram.Bot.create_invoice_link.description` parameter of + :meth:`telegram.Bot.create_invoice_link`. + """ + MAX_DESCRIPTION_LENGTH = 255 + """:obj:`int`: Maximum number of characters in a :obj:`str` passed as: + + * :paramref:`~telegram.InputInvoiceMessageContent.description` parameter of + :class:`telegram.InputInvoiceMessageContent` + * :paramref:`~telegram.Bot.send_invoice.description` parameter of + :meth:`telegram.Bot.send_invoice`. + * :paramref:`~telegram.Bot.create_invoice_link.description` parameter of + :meth:`telegram.Bot.create_invoice_link`. + """ + MIN_PAYLOAD_LENGTH = 1 + """:obj:`int`: Minimum amount of bytes in a :obj:`str` passed as: + + * :paramref:`~telegram.InputInvoiceMessageContent.payload` parameter of + :class:`telegram.InputInvoiceMessageContent` + * :paramref:`~telegram.Bot.send_invoice.payload` parameter of + :meth:`telegram.Bot.send_invoice`. + * :paramref:`~telegram.Bot.create_invoice_link.payload` parameter of + :meth:`telegram.Bot.create_invoice_link`. + """ + MAX_PAYLOAD_LENGTH = 128 + """:obj:`int`: Maximum amount of bytes in a :obj:`str` passed as: + + * :paramref:`~telegram.InputInvoiceMessageContent.payload` parameter of + :class:`telegram.InputInvoiceMessageContent` + * :paramref:`~telegram.Bot.send_invoice.payload` parameter of + :meth:`telegram.Bot.send_invoice`. + * :paramref:`~telegram.Bot.create_invoice_link.payload` parameter of + :meth:`telegram.Bot.create_invoice_link`. + """ + MAX_TIP_AMOUNTS = 4 + """:obj:`int`: Maximum length of a :obj:`Sequence` passed as: + + * :paramref:`~telegram.Bot.send_invoice.suggested_tip_amounts` parameter of + :meth:`telegram.Bot.send_invoice`. + * :paramref:`~telegram.Bot.create_invoice_link.suggested_tip_amounts` parameter of + :meth:`telegram.Bot.create_invoice_link`. + """ + + +class UserProfilePhotosLimit(IntEnum): + """This enum contains limitations for :paramref:`telegram.Bot.get_user_profile_photos.limit`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_LIMIT = 1 + """:obj:`int`: Minimum value allowed for + :paramref:`~telegram.Bot.get_user_profile_photos.limit` parameter of + :meth:`telegram.Bot.get_user_profile_photos`. + """ + MAX_LIMIT = 100 + """:obj:`int`: Maximum value allowed for + :paramref:`~telegram.Bot.get_user_profile_photos.limit` parameter of + :meth:`telegram.Bot.get_user_profile_photos`. + """ + + +class WebhookLimit(IntEnum): + """This enum contains limitations for :paramref:`telegram.Bot.set_webhook.max_connections` and + :paramref:`telegram.Bot.set_webhook.secret_token`. The enum members of this enumeration are + instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_CONNECTIONS_LIMIT = 1 + """:obj:`int`: Minimum value allowed for the + :paramref:`~telegram.Bot.set_webhook.max_connections` parameter of + :meth:`telegram.Bot.set_webhook`. + """ + MAX_CONNECTIONS_LIMIT = 100 + """:obj:`int`: Maximum value allowed for the + :paramref:`~telegram.Bot.set_webhook.max_connections` parameter of + :meth:`telegram.Bot.set_webhook`. + """ + MIN_SECRET_TOKEN_LENGTH = 1 + """:obj:`int`: Minimum length of the secret token for the + :paramref:`~telegram.Bot.set_webhook.secret_token` parameter of + :meth:`telegram.Bot.set_webhook`. + """ + MAX_SECRET_TOKEN_LENGTH = 256 + """:obj:`int`: Maximum length of the secret token for the + :paramref:`~telegram.Bot.set_webhook.secret_token` parameter of + :meth:`telegram.Bot.set_webhook`. + """ + + +class ForumTopicLimit(IntEnum): + """This enum contains limitations for :paramref:`telegram.Bot.create_forum_topic.name` and + :paramref:`telegram.Bot.edit_forum_topic.name`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_NAME_LENGTH = 1 + """:obj:`int`: Minimum length of a :obj:`str` passed as: + + * :paramref:`~telegram.Bot.create_forum_topic.name` parameter of + :meth:`telegram.Bot.create_forum_topic` + * :paramref:`~telegram.Bot.edit_forum_topic.name` parameter of + :meth:`telegram.Bot.edit_forum_topic` + * :paramref:`~telegram.Bot.edit_general_forum_topic.name` parameter of + :meth:`telegram.Bot.edit_general_forum_topic` + """ + MAX_NAME_LENGTH = 128 + """:obj:`int`: Maximum length of a :obj:`str` passed as: + + * :paramref:`~telegram.Bot.create_forum_topic.name` parameter of + :meth:`telegram.Bot.create_forum_topic` + * :paramref:`~telegram.Bot.edit_forum_topic.name` parameter of + :meth:`telegram.Bot.edit_forum_topic` + * :paramref:`~telegram.Bot.edit_general_forum_topic.name` parameter of + :meth:`telegram.Bot.edit_general_forum_topic` + """ + + +class ReactionType(StringEnum): + """This enum contains the available types of :class:`telegram.ReactionType`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.8 + """ + + __slots__ = () + + EMOJI = "emoji" + """:obj:`str`: A :class:`telegram.ReactionType` with a normal emoji.""" + CUSTOM_EMOJI = "custom_emoji" + """:obj:`str`: A :class:`telegram.ReactionType` with a custom emoji.""" + PAID = "paid" + """:obj:`str`: A :class:`telegram.ReactionType` with a paid reaction. + + .. versionadded:: 21.5 + """ + + +class ReactionEmoji(StringEnum): + """This enum contains the available emojis of :class:`telegram.ReactionTypeEmoji`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 20.8 + """ + + __slots__ = () + + THUMBS_UP = "👍" + """:obj:`str`: Thumbs Up""" + THUMBS_DOWN = "👎" + """:obj:`str`: Thumbs Down""" + RED_HEART = "❤" + """:obj:`str`: Red Heart""" + FIRE = "🔥" + """:obj:`str`: Fire""" + SMILING_FACE_WITH_HEARTS = "🥰" + """:obj:`str`: Smiling Face with Hearts""" + CLAPPING_HANDS = "👏" + """:obj:`str`: Clapping Hands""" + GRINNING_FACE_WITH_SMILING_EYES = "😁" + """:obj:`str`: Grinning face with smiling eyes""" + THINKING_FACE = "🤔" + """:obj:`str`: Thinking face""" + SHOCKED_FACE_WITH_EXPLODING_HEAD = "🤯" + """:obj:`str`: Shocked face with exploding head""" + FACE_SCREAMING_IN_FEAR = "😱" + """:obj:`str`: Face screaming in fear""" + SERIOUS_FACE_WITH_SYMBOLS_COVERING_MOUTH = "🤬" + """:obj:`str`: Serious face with symbols covering mouth""" + CRYING_FACE = "😢" + """:obj:`str`: Crying face""" + PARTY_POPPER = "🎉" + """:obj:`str`: Party popper""" + GRINNING_FACE_WITH_STAR_EYES = "🤩" + """:obj:`str`: Grinning face with star eyes""" + FACE_WITH_OPEN_MOUTH_VOMITING = "🤮" + """:obj:`str`: Face with open mouth vomiting""" + PILE_OF_POO = "💩" + """:obj:`str`: Pile of poo""" + PERSON_WITH_FOLDED_HANDS = "🙏" + """:obj:`str`: Person with folded hands""" + OK_HAND_SIGN = "👌" + """:obj:`str`: Ok hand sign""" + DOVE_OF_PEACE = "🕊" + """:obj:`str`: Dove of peace""" + CLOWN_FACE = "🤡" + """:obj:`str`: Clown face""" + YAWNING_FACE = "🥱" + """:obj:`str`: Yawning face""" + FACE_WITH_UNEVEN_EYES_AND_WAVY_MOUTH = "🥴" + """:obj:`str`: Face with uneven eyes and wavy mouth""" + SMILING_FACE_WITH_HEART_SHAPED_EYES = "😍" + """:obj:`str`: Smiling face with heart-shaped eyes""" + SPOUTING_WHALE = "🐳" + """:obj:`str`: Spouting whale""" + HEART_ON_FIRE = "❤️‍🔥" + """:obj:`str`: Heart on fire""" + NEW_MOON_WITH_FACE = "🌚" + """:obj:`str`: New moon with face""" + HOT_DOG = "🌭" + """:obj:`str`: Hot dog""" + HUNDRED_POINTS_SYMBOL = "💯" + """:obj:`str`: Hundred points symbol""" + ROLLING_ON_THE_FLOOR_LAUGHING = "🤣" + """:obj:`str`: Rolling on the floor laughing""" + HIGH_VOLTAGE_SIGN = "⚡" + """:obj:`str`: High voltage sign""" + BANANA = "🍌" + """:obj:`str`: Banana""" + TROPHY = "🏆" + """:obj:`str`: Trophy""" + BROKEN_HEART = "💔" + """:obj:`str`: Broken heart""" + FACE_WITH_ONE_EYEBROW_RAISED = "🤨" + """:obj:`str`: Face with one eyebrow raised""" + NEUTRAL_FACE = "😐" + """:obj:`str`: Neutral face""" + STRAWBERRY = "🍓" + """:obj:`str`: Strawberry""" + BOTTLE_WITH_POPPING_CORK = "🍾" + """:obj:`str`: Bottle with popping cork""" + KISS_MARK = "💋" + """:obj:`str`: Kiss mark""" + REVERSED_HAND_WITH_MIDDLE_FINGER_EXTENDED = "🖕" + """:obj:`str`: Reversed hand with middle finger extended""" + SMILING_FACE_WITH_HORNS = "😈" + """:obj:`str`: Smiling face with horns""" + SLEEPING_FACE = "😴" + """:obj:`str`: Sleeping face""" + LOUDLY_CRYING_FACE = "😭" + """:obj:`str`: Loudly crying face""" + NERD_FACE = "🤓" + """:obj:`str`: Nerd face""" + GHOST = "👻" + """:obj:`str`: Ghost""" + MAN_TECHNOLOGIST = "👨‍💻" + """:obj:`str`: Man Technologist""" + EYES = "👀" + """:obj:`str`: Eyes""" + JACK_O_LANTERN = "🎃" + """:obj:`str`: Jack-o-lantern""" + SEE_NO_EVIL_MONKEY = "🙈" + """:obj:`str`: See-no-evil monkey""" + SMILING_FACE_WITH_HALO = "😇" + """:obj:`str`: Smiling face with halo""" + FEARFUL_FACE = "😨" + """:obj:`str`: Fearful face""" + HANDSHAKE = "🤝" + """:obj:`str`: Handshake""" + WRITING_HAND = "✍" + """:obj:`str`: Writing hand""" + HUGGING_FACE = "🤗" + """:obj:`str`: Hugging face""" + SALUTING_FACE = "🫡" + """:obj:`str`: Saluting face""" + FATHER_CHRISTMAS = "🎅" + """:obj:`str`: Father christmas""" + CHRISTMAS_TREE = "🎄" + """:obj:`str`: Christmas tree""" + SNOWMAN = "☃" + """:obj:`str`: Snowman""" + NAIL_POLISH = "💅" + """:obj:`str`: Nail polish""" + GRINNING_FACE_WITH_ONE_LARGE_AND_ONE_SMALL_EYE = "🤪" + """:obj:`str`: Grinning face with one large and one small eye""" + MOYAI = "🗿" + """:obj:`str`: Moyai""" + SQUARED_COOL = "🆒" + """:obj:`str`: Squared cool""" + HEART_WITH_ARROW = "💘" + """:obj:`str`: Heart with arrow""" + HEAR_NO_EVIL_MONKEY = "🙉" + """:obj:`str`: Hear-no-evil monkey""" + UNICORN_FACE = "🦄" + """:obj:`str`: Unicorn face""" + FACE_THROWING_A_KISS = "😘" + """:obj:`str`: Face throwing a kiss""" + PILL = "💊" + """:obj:`str`: Pill""" + SPEAK_NO_EVIL_MONKEY = "🙊" + """:obj:`str`: Speak-no-evil monkey""" + SMILING_FACE_WITH_SUNGLASSES = "😎" + """:obj:`str`: Smiling face with sunglasses""" + ALIEN_MONSTER = "👾" + """:obj:`str`: Alien monster""" + MAN_SHRUGGING = "🤷‍♂️" + """:obj:`str`: Man Shrugging""" + SHRUG = "🤷" + """:obj:`str`: Shrug""" + WOMAN_SHRUGGING = "🤷‍♀️" + """:obj:`str`: Woman Shrugging""" + POUTING_FACE = "😡" + """:obj:`str`: Pouting face""" + + +class BackgroundTypeType(StringEnum): + """This enum contains the available types of :class:`telegram.BackgroundType`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 21.2 + """ + + __slots__ = () + + FILL = "fill" + """:obj:`str`: A :class:`telegram.BackgroundType` with fill background.""" + WALLPAPER = "wallpaper" + """:obj:`str`: A :class:`telegram.BackgroundType` with wallpaper background.""" + PATTERN = "pattern" + """:obj:`str`: A :class:`telegram.BackgroundType` with pattern background.""" + CHAT_THEME = "chat_theme" + """:obj:`str`: A :class:`telegram.BackgroundType` with chat_theme background.""" + + +class BackgroundFillType(StringEnum): + """This enum contains the available types of :class:`telegram.BackgroundFill`. The enum + members of this enumeration are instances of :class:`str` and can be treated as such. + + .. versionadded:: 21.2 + """ + + __slots__ = () + + SOLID = "solid" + """:obj:`str`: A :class:`telegram.BackgroundFill` with solid fill.""" + GRADIENT = "gradient" + """:obj:`str`: A :class:`telegram.BackgroundFill` with gradient fill.""" + FREEFORM_GRADIENT = "freeform_gradient" + """:obj:`str`: A :class:`telegram.BackgroundFill` with freeform_gradient fill.""" + + +class ChatSubscriptionLimit(IntEnum): + """This enum contains limitations for + :paramref:`telegram.Bot.create_chat_subscription_invite_link.subscription_period` and + :paramref:`telegram.Bot.create_chat_subscription_invite_link.subscription_price`. + The enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 21.5 + """ + + __slots__ = () + + SUBSCRIPTION_PERIOD = 2592000 + """:obj:`int`: The number of seconds the subscription will be active.""" + MIN_PRICE = 1 + """:obj:`int`: Amount of stars a user pays, minimum amount the subscription can be set to.""" + MAX_PRICE = 2500 + """:obj:`int`: Amount of stars a user pays, maximum amount the subscription can be set to.""" diff --git a/error.py b/error.py new file mode 100644 index 0000000000000000000000000000000000000000..6dcc509a8d466bf42b8dfb1d457c42146974fe67 --- /dev/null +++ b/error.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains classes that represent Telegram errors. + +.. versionchanged:: 20.0 + Replaced ``Unauthorized`` by :class:`Forbidden`. +""" + +__all__ = ( + "BadRequest", + "ChatMigrated", + "Conflict", + "EndPointNotFound", + "Forbidden", + "InvalidToken", + "NetworkError", + "PassportDecryptionError", + "RetryAfter", + "TelegramError", + "TimedOut", +) + +from typing import Optional, Tuple, Union + + +def _lstrip_str(in_s: str, lstr: str) -> str: + """ + Args: + in_s (:obj:`str`): in string + lstr (:obj:`str`): substr to strip from left side + + Returns: + :obj:`str`: The stripped string. + + """ + return in_s[len(lstr) :] if in_s.startswith(lstr) else in_s + + +class TelegramError(Exception): + """ + Base class for Telegram errors. + + Tip: + Objects of this type can be serialized via Python's :mod:`pickle` module and pickled + objects from one version of PTB are usually loadable in future versions. However, we can + not guarantee that this compatibility will always be provided. At least a manual one-time + conversion of the data may be needed on major updates of the library. + + .. seealso:: :wiki:`Exceptions, Warnings and Logging ` + """ + + __slots__ = ("message",) + + def __init__(self, message: str): + super().__init__() + + msg = _lstrip_str(message, "Error: ") + msg = _lstrip_str(msg, "[Error]: ") + msg = _lstrip_str(msg, "Bad Request: ") + if msg != message: + # api_error - capitalize the msg... + msg = msg.capitalize() + self.message: str = msg + + def __str__(self) -> str: + """Gives the string representation of exceptions message. + + Returns: + :obj:`str` + """ + return self.message + + def __repr__(self) -> str: + """Gives an unambiguous string representation of the exception. + + Returns: + :obj:`str` + """ + return f"{self.__class__.__name__}('{self.message}')" + + def __reduce__(self) -> Tuple[type, Tuple[str]]: + """Defines how to serialize the exception for pickle. + + .. seealso:: + :py:meth:`object.__reduce__`, :mod:`pickle`. + + Returns: + :obj:`tuple` + """ + return self.__class__, (self.message,) + + +class Forbidden(TelegramError): + """Raised when the bot has not enough rights to perform the requested action. + + Examples: + :any:`Raw API Bot ` + + .. versionchanged:: 20.0 + This class was previously named ``Unauthorized``. + """ + + __slots__ = () + + +class InvalidToken(TelegramError): + """Raised when the token is invalid. + + Args: + message (:obj:`str`, optional): Any additional information about the exception. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + def __init__(self, message: Optional[str] = None) -> None: + super().__init__("Invalid token" if message is None else message) + + +class EndPointNotFound(TelegramError): + """Raised when the requested endpoint is not found. Only relevant for + :meth:`telegram.Bot.do_api_request`. + + .. versionadded:: 20.8 + """ + + __slots__ = () + + +class NetworkError(TelegramError): + """Base class for exceptions due to networking errors. + + Tip: + This exception (and its subclasses) usually originates from the networking backend + used by :class:`~telegram.request.HTTPXRequest`, or a custom implementation of + :class:`~telegram.request.BaseRequest`. In this case, the original exception can be + accessed via the ``__cause__`` + `attribute `_. + + Examples: + :any:`Raw API Bot ` + + .. seealso:: + :wiki:`Handling network errors ` + """ + + __slots__ = () + + +class BadRequest(NetworkError): + """Raised when Telegram could not process the request correctly.""" + + __slots__ = () + + +class TimedOut(NetworkError): + """Raised when a request took too long to finish. + + .. seealso:: + :wiki:`Handling network errors ` + + Args: + message (:obj:`str`, optional): Any additional information about the exception. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + def __init__(self, message: Optional[str] = None) -> None: + super().__init__(message or "Timed out") + + +class ChatMigrated(TelegramError): + """ + Raised when the requested group chat migrated to supergroup and has a new chat id. + + .. seealso:: + :wiki:`Storing Bot, User and Chat Related Data ` + + Args: + new_chat_id (:obj:`int`): The new chat id of the group. + + Attributes: + new_chat_id (:obj:`int`): The new chat id of the group. + + """ + + __slots__ = ("new_chat_id",) + + def __init__(self, new_chat_id: int): + super().__init__(f"Group migrated to supergroup. New chat id: {new_chat_id}") + self.new_chat_id: int = new_chat_id + + def __reduce__(self) -> Tuple[type, Tuple[int]]: # type: ignore[override] + return self.__class__, (self.new_chat_id,) + + +class RetryAfter(TelegramError): + """ + Raised when flood limits where exceeded. + + .. versionchanged:: 20.0 + :attr:`retry_after` is now an integer to comply with the Bot API. + + Args: + retry_after (:obj:`int`): Time in seconds, after which the bot can retry the request. + + Attributes: + retry_after (:obj:`int`): Time in seconds, after which the bot can retry the request. + + """ + + __slots__ = ("retry_after",) + + def __init__(self, retry_after: int): + super().__init__(f"Flood control exceeded. Retry in {retry_after} seconds") + self.retry_after: int = retry_after + + def __reduce__(self) -> Tuple[type, Tuple[float]]: # type: ignore[override] + return self.__class__, (self.retry_after,) + + +class Conflict(TelegramError): + """Raised when a long poll or webhook conflicts with another one.""" + + __slots__ = () + + def __reduce__(self) -> Tuple[type, Tuple[str]]: + return self.__class__, (self.message,) + + +class PassportDecryptionError(TelegramError): + """Something went wrong with decryption. + + .. versionchanged:: 20.0 + This class was previously named ``TelegramDecryptionError`` and was available via + ``telegram.TelegramDecryptionError``. + """ + + __slots__ = ("_msg",) + + def __init__(self, message: Union[str, Exception]): + super().__init__(f"PassportDecryptionError: {message}") + self._msg = str(message) + + def __reduce__(self) -> Tuple[type, Tuple[str]]: + return self.__class__, (self._msg,) diff --git a/helpers.py b/helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..d303b64a78ba6fb69cc5d613c1f73ca80eeb0feb --- /dev/null +++ b/helpers.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains convenience helper functions. + +.. versionchanged:: 20.0 + Previously, the contents of this module were available through the (no longer existing) + module ``telegram.utils.helpers``. +""" + +__all__ = ( + "create_deep_linked_url", + "effective_message_type", + "escape_markdown", + "mention_html", + "mention_markdown", +) + +import re +from html import escape +from typing import TYPE_CHECKING, Optional, Union + +from telegram._utils.types import MarkdownVersion +from telegram.constants import MessageType + +if TYPE_CHECKING: + from telegram import Message, Update + + +def escape_markdown( + text: str, version: MarkdownVersion = 1, entity_type: Optional[str] = None +) -> str: + """Helper function to escape telegram markup symbols. + + .. versionchanged:: 20.3 + Custom emoji entity escaping is now supported. + + Args: + text (:obj:`str`): The text. + version (:obj:`int` | :obj:`str`): Use to specify the version of telegrams Markdown. + Either ``1`` or ``2``. Defaults to ``1``. + entity_type (:obj:`str`, optional): For the entity types + :tg-const:`telegram.MessageEntity.PRE`, :tg-const:`telegram.MessageEntity.CODE` and + the link part of :tg-const:`telegram.MessageEntity.TEXT_LINK` and + :tg-const:`telegram.MessageEntity.CUSTOM_EMOJI`, only certain characters need to be + escaped in :tg-const:`telegram.constants.ParseMode.MARKDOWN_V2`. See the `official API + documentation `_ for details. + Only valid in combination with ``version=2``, will be ignored else. + """ + if int(version) == 1: + escape_chars = r"_*`[" + elif int(version) == 2: + if entity_type in ["pre", "code"]: + escape_chars = r"\`" + elif entity_type in ["text_link", "custom_emoji"]: + escape_chars = r"\)" + else: + escape_chars = r"\_*[]()~`>#+-=|{}.!" + else: + raise ValueError("Markdown version must be either 1 or 2!") + + return re.sub(f"([{re.escape(escape_chars)}])", r"\\\1", text) + + +def mention_html(user_id: Union[int, str], name: str) -> str: + """ + Helper function to create a user mention as HTML tag. + + Args: + user_id (:obj:`int`): The user's id which you want to mention. + name (:obj:`str`): The name the mention is showing. + + Returns: + :obj:`str`: The inline mention for the user as HTML. + """ + return f'{escape(name)}' + + +def mention_markdown(user_id: Union[int, str], name: str, version: MarkdownVersion = 1) -> str: + """ + Helper function to create a user mention in Markdown syntax. + + Args: + user_id (:obj:`int`): The user's id which you want to mention. + name (:obj:`str`): The name the mention is showing. + version (:obj:`int` | :obj:`str`): Use to specify the version of Telegram's Markdown. + Either ``1`` or ``2``. Defaults to ``1``. + + Returns: + :obj:`str`: The inline mention for the user as Markdown. + """ + tg_link = f"tg://user?id={user_id}" + if version == 1: + return f"[{name}]({tg_link})" + return f"[{escape_markdown(name, version=version)}]({tg_link})" + + +def effective_message_type(entity: Union["Message", "Update"]) -> Optional[str]: + """ + Extracts the type of message as a string identifier from a :class:`telegram.Message` or a + :class:`telegram.Update`. + + Args: + entity (:class:`telegram.Update` | :class:`telegram.Message`): The ``update`` or + ``message`` to extract from. + + Returns: + :obj:`str` | :obj:`None`: One of :class:`telegram.constants.MessageType` if the entity + contains a message that matches one of those types. :obj:`None` otherwise. + + """ + # Importing on file-level yields cyclic Import Errors + from telegram import Message, Update # pylint: disable=import-outside-toplevel + + if isinstance(entity, Message): + message = entity + elif isinstance(entity, Update): + if not entity.effective_message: + return None + message = entity.effective_message + else: + raise TypeError(f"The entity is neither Message nor Update (got: {type(entity)})") + + for message_type in MessageType: + if message[message_type]: + return message_type + + return None + + +def create_deep_linked_url( + bot_username: str, payload: Optional[str] = None, group: bool = False +) -> str: + """ + Creates a deep-linked URL for this :paramref:`~create_deep_linked_url.bot_username` with the + specified :paramref:`~create_deep_linked_url.payload`. See + https://core.telegram.org/bots/features#deep-linking to learn more. + + The :paramref:`~create_deep_linked_url.payload` may consist of the following characters: + ``A-Z, a-z, 0-9, _, -`` + + Note: + Works well in conjunction with + ``CommandHandler("start", callback, filters=filters.Regex('payload'))`` + + Examples: + * ``create_deep_linked_url(bot.get_me().username, "some-params")`` + * :any:`Deep Linking ` + + Args: + bot_username (:obj:`str`): The username to link to. + payload (:obj:`str`, optional): Parameters to encode in the created URL. + group (:obj:`bool`, optional): If :obj:`True` the user is prompted to select a group to + add the bot to. If :obj:`False`, opens a one-on-one conversation with the bot. + Defaults to :obj:`False`. + + Returns: + :obj:`str`: An URL to start the bot with specific parameters. + + Raises: + :exc:`ValueError`: If the length of the :paramref:`payload` exceeds 64 characters, + contains invalid characters, or if the :paramref:`bot_username` is less than 4 + characters. + """ + if bot_username is None or len(bot_username) <= 3: + raise ValueError("You must provide a valid bot_username.") + + base_url = f"https://t.me/{bot_username}" + if not payload: + return base_url + + if len(payload) > 64: + raise ValueError("The deep-linking payload must not exceed 64 characters.") + + if not re.match(r"^[A-Za-z0-9_-]+$", payload): + raise ValueError( + "Only the following characters are allowed for deep-linked " + "URLs: A-Z, a-z, 0-9, _ and -" + ) + + key = "startgroup" if group else "start" + + return f"{base_url}?{key}={payload}" diff --git a/inlinekeyboardbutton.py b/inlinekeyboardbutton.py new file mode 100644 index 0000000000000000000000000000000000000000..cff4df66a21a5956a687a29b006bbb3949c955a5 --- /dev/null +++ b/inlinekeyboardbutton.py @@ -0,0 +1,328 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram InlineKeyboardButton.""" + +from typing import TYPE_CHECKING, Final, Optional, Union + +from telegram import constants +from telegram._games.callbackgame import CallbackGame +from telegram._loginurl import LoginUrl +from telegram._switchinlinequerychosenchat import SwitchInlineQueryChosenChat +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict +from telegram._webappinfo import WebAppInfo + +if TYPE_CHECKING: + from telegram import Bot + + +class InlineKeyboardButton(TelegramObject): + """This object represents one button of an inline keyboard. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`text`, :attr:`url`, :attr:`login_url`, :attr:`callback_data`, + :attr:`switch_inline_query`, :attr:`switch_inline_query_current_chat`, :attr:`callback_game`, + :attr:`web_app` and :attr:`pay` are equal. + + Note: + * Exactly one of the optional fields must be used to specify type of the button. + * Mind that :attr:`callback_game` is not + working as expected. Putting a game short name in it might, but is not guaranteed to + work. + * If your bot allows for arbitrary callback data, in keyboards returned in a response + from telegram, :attr:`callback_data` may be an instance of + :class:`telegram.ext.InvalidCallbackData`. This will be the case, if the data + associated with the button was already deleted. + + .. versionadded:: 13.6 + + * Since Bot API 5.5, it's now allowed to mention users by their ID in inline keyboards. + This will only work in Telegram versions released after December 7, 2021. + Older clients will display *unsupported message*. + + Warning: + * If your bot allows your arbitrary callback data, buttons whose callback data is a + non-hashable object will become unhashable. Trying to evaluate ``hash(button)`` will + result in a :class:`TypeError`. + + .. versionchanged:: 13.6 + + * After Bot API 6.1, only ``HTTPS`` links will be allowed in :paramref:`login_url`. + + Examples: + * :any:`Inline Keyboard 1 ` + * :any:`Inline Keyboard 2 ` + + .. seealso:: :class:`telegram.InlineKeyboardMarkup` + + .. versionchanged:: 20.0 + :attr:`web_app` is considered as well when comparing objects of this type in terms of + equality. + + Args: + text (:obj:`str`): Label text on the button. + url (:obj:`str`, optional): HTTP or tg:// url to be opened when the button is pressed. + Links ``tg://user?id=`` can be used to mention a user by + their ID without using a username, if this is allowed by their privacy settings. + + .. versionchanged:: 13.9 + You can now mention a user using ``tg://user?id=``. + login_url (:class:`telegram.LoginUrl`, optional): An ``HTTPS`` URL used to automatically + authorize the user. Can be used as a replacement for the Telegram Login Widget. + + Caution: + Only ``HTTPS`` links are allowed after Bot API 6.1. + callback_data (:obj:`str` | :obj:`object`, optional): Data to be sent in a callback query + to the bot when the button is pressed, UTF-8 + :tg-const:`telegram.InlineKeyboardButton.MIN_CALLBACK_DATA`- + :tg-const:`telegram.InlineKeyboardButton.MAX_CALLBACK_DATA` bytes. + If the bot instance allows arbitrary callback data, anything can be passed. + + Tip: + The value entered here will be available in :attr:`telegram.CallbackQuery.data`. + + .. seealso:: :wiki:`Arbitrary callback_data ` + + web_app (:class:`telegram.WebAppInfo`, optional): Description of the `Web App + `_ that will be launched when the user presses + the button. The Web App will be able to send an arbitrary message on behalf of the user + using the method :meth:`~telegram.Bot.answer_web_app_query`. Available only in + private chats between a user and the bot. Not supported for messages sent on behalf of + a Telegram Business account. + + .. versionadded:: 20.0 + switch_inline_query (:obj:`str`, optional): If set, pressing the button will prompt the + user to select one of their chats, open that chat and insert the bot's username and the + specified inline query in the input field. May be empty, in which case just the bot's + username will be inserted. Not supported for messages sent on behalf of a Telegram + Business account. + + Tip: + This is similar to the parameter :paramref:`switch_inline_query_chosen_chat`, + but gives no control over which chats can be selected. + switch_inline_query_current_chat (:obj:`str`, optional): If set, pressing the button will + insert the bot's username and the specified inline query in the current chat's input + field. May be empty, in which case only the bot's username will be inserted. + + This offers a quick way for the user to open your bot in inline mode in the same chat + - good for selecting something from multiple options. Not supported in channels and for + messages sent on behalf of a Telegram Business account. + callback_game (:class:`telegram.CallbackGame`, optional): Description of the game that will + be launched when the user presses the button + + Note: + This type of button **must** always be the first button in the first row. + pay (:obj:`bool`, optional): Specify :obj:`True`, to send a Pay button. + Substrings ``“⭐️”`` and ``“XTR”`` in the buttons's text will be replaced with a + Telegram Star icon. + + Note: + This type of button **must** always be the first button in the first row and can + only be used in invoice messages. + switch_inline_query_chosen_chat (:class:`telegram.SwitchInlineQueryChosenChat`, optional): + If set, pressing the button will prompt the user to select one of their chats of the + specified type, open that chat and insert the bot's username and the specified inline + query in the input field. Not supported for messages sent on behalf of a Telegram + Business account. + + .. versionadded:: 20.3 + + Tip: + This is similar to :paramref:`switch_inline_query`, but gives more control on + which chats can be selected. + + Caution: + The PTB team has discovered that this field works correctly only if your Telegram + client is released after April 20th 2023. + + Attributes: + text (:obj:`str`): Label text on the button. + url (:obj:`str`): Optional. HTTP or tg:// url to be opened when the button is pressed. + Links ``tg://user?id=`` can be used to mention a user by + their ID without using a username, if this is allowed by their privacy settings. + + .. versionchanged:: 13.9 + You can now mention a user using ``tg://user?id=``. + login_url (:class:`telegram.LoginUrl`): Optional. An ``HTTPS`` URL used to automatically + authorize the user. Can be used as a replacement for the Telegram Login Widget. + + Caution: + Only ``HTTPS`` links are allowed after Bot API 6.1. + callback_data (:obj:`str` | :obj:`object`): Optional. Data to be sent in a callback query + to the bot when the button is pressed, UTF-8 + :tg-const:`telegram.InlineKeyboardButton.MIN_CALLBACK_DATA`- + :tg-const:`telegram.InlineKeyboardButton.MAX_CALLBACK_DATA` bytes. + web_app (:class:`telegram.WebAppInfo`): Optional. Description of the `Web App + `_ that will be launched when the user presses + the button. The Web App will be able to send an arbitrary message on behalf of the user + using the method :meth:`~telegram.Bot.answer_web_app_query`. Available only in + private chats between a user and the bot. Not supported for messages sent on behalf of + a Telegram Business account. + + .. versionadded:: 20.0 + switch_inline_query (:obj:`str`): Optional. If set, pressing the button will prompt the + user to select one of their chats, open that chat and insert the bot's username and the + specified inline query in the input field. May be empty, in which case just the bot's + username will be inserted. Not supported for messages sent on behalf of a Telegram + Business account. + + Tip: + This is similar to the parameter :paramref:`switch_inline_query_chosen_chat`, + but gives no control over which chats can be selected. + switch_inline_query_current_chat (:obj:`str`): Optional. If set, pressing the button will + insert the bot's username and the specified inline query in the current chat's input + field. May be empty, in which case only the bot's username will be inserted. + + This offers a quick way for the user to open your bot in inline mode in the same chat + - good for selecting something from multiple options. Not supported in channels and for + messages sent on behalf of a Telegram Business account. + callback_game (:class:`telegram.CallbackGame`): Optional. Description of the game that will + be launched when the user presses the button. + + Note: + This type of button **must** always be the first button in the first row. + pay (:obj:`bool`): Optional. Specify :obj:`True`, to send a Pay button. + Substrings ``“⭐️”`` and ``“XTR”`` in the buttons's text will be replaced with a + Telegram Star icon. + + Note: + This type of button **must** always be the first button in the first row and can + only be used in invoice messages. + switch_inline_query_chosen_chat (:class:`telegram.SwitchInlineQueryChosenChat`): Optional. + If set, pressing the button will prompt the user to select one of their chats of the + specified type, open that chat and insert the bot's username and the specified inline + query in the input field. Not supported for messages sent on behalf of a Telegram + Business account. + + .. versionadded:: 20.3 + + Tip: + This is similar to :attr:`switch_inline_query`, but gives more control on + which chats can be selected. + + Caution: + The PTB team has discovered that this field works correctly only if your Telegram + client is released after April 20th 2023. + """ + + __slots__ = ( + "callback_data", + "callback_game", + "login_url", + "pay", + "switch_inline_query", + "switch_inline_query_chosen_chat", + "switch_inline_query_current_chat", + "text", + "url", + "web_app", + ) + + def __init__( + self, + text: str, + url: Optional[str] = None, + callback_data: Optional[Union[str, object]] = None, + switch_inline_query: Optional[str] = None, + switch_inline_query_current_chat: Optional[str] = None, + callback_game: Optional[CallbackGame] = None, + pay: Optional[bool] = None, + login_url: Optional[LoginUrl] = None, + web_app: Optional[WebAppInfo] = None, + switch_inline_query_chosen_chat: Optional[SwitchInlineQueryChosenChat] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.text: str = text + + # Optionals + self.url: Optional[str] = url + self.login_url: Optional[LoginUrl] = login_url + self.callback_data: Optional[Union[str, object]] = callback_data + self.switch_inline_query: Optional[str] = switch_inline_query + self.switch_inline_query_current_chat: Optional[str] = switch_inline_query_current_chat + self.callback_game: Optional[CallbackGame] = callback_game + self.pay: Optional[bool] = pay + self.web_app: Optional[WebAppInfo] = web_app + self.switch_inline_query_chosen_chat: Optional[SwitchInlineQueryChosenChat] = ( + switch_inline_query_chosen_chat + ) + self._id_attrs = () + self._set_id_attrs() + + self._freeze() + + def _set_id_attrs(self) -> None: + self._id_attrs = ( + self.text, + self.url, + self.login_url, + self.callback_data, + self.web_app, + self.switch_inline_query, + self.switch_inline_query_current_chat, + self.callback_game, + self.pay, + ) + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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 + ) + + return super().de_json(data=data, bot=bot) + + def update_callback_data(self, callback_data: Union[str, object]) -> None: + """ + Sets :attr:`callback_data` to the passed object. Intended to be used by + :class:`telegram.ext.CallbackDataCache`. + + .. versionadded:: 13.6 + + Args: + callback_data (:class:`object`): The new callback data. + """ + with self._unfrozen(): + self.callback_data = callback_data + self._set_id_attrs() + + MIN_CALLBACK_DATA: Final[int] = constants.InlineKeyboardButtonLimit.MIN_CALLBACK_DATA + """:const:`telegram.constants.InlineKeyboardButtonLimit.MIN_CALLBACK_DATA` + + .. versionadded:: 20.0 + """ + MAX_CALLBACK_DATA: Final[int] = constants.InlineKeyboardButtonLimit.MAX_CALLBACK_DATA + """:const:`telegram.constants.InlineKeyboardButtonLimit.MAX_CALLBACK_DATA` + + .. versionadded:: 20.0 + """ diff --git a/inlinekeyboardmarkup.py b/inlinekeyboardmarkup.py new file mode 100644 index 0000000000000000000000000000000000000000..6857e4d8e3a85614e97ab4460c3bf9b5911a41ec --- /dev/null +++ b/inlinekeyboardmarkup.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram InlineKeyboardMarkup.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardbutton import InlineKeyboardButton +from telegram._telegramobject import TelegramObject +from telegram._utils.markup import check_keyboard_type +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class InlineKeyboardMarkup(TelegramObject): + """ + This object represents an inline keyboard that appears right next to the message it belongs to. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their size of :attr:`inline_keyboard` and all the buttons are equal. + + .. figure:: https://core.telegram.org/file/464001863/110f3/I47qTXAD9Z4.120010/e0\ + ea04f66357b640ec + :align: center + + An inline keyboard on a message + + .. seealso:: + Another kind of keyboard would be the :class:`telegram.ReplyKeyboardMarkup`. + + Examples: + * :any:`Inline Keyboard 1 ` + * :any:`Inline Keyboard 2 ` + + Args: + inline_keyboard (Sequence[Sequence[:class:`telegram.InlineKeyboardButton`]]): Sequence of + button rows, each represented by a sequence of :class:`~telegram.InlineKeyboardButton` + objects. + + .. versionchanged:: 20.0 + |sequenceclassargs| + + Attributes: + inline_keyboard (Tuple[Tuple[:class:`telegram.InlineKeyboardButton`]]): Tuple of + button rows, each represented by a tuple of :class:`~telegram.InlineKeyboardButton` + objects. + + .. versionchanged:: 20.0 + |tupleclassattrs| + + """ + + __slots__ = ("inline_keyboard",) + + def __init__( + self, + inline_keyboard: Sequence[Sequence[InlineKeyboardButton]], + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + if not check_keyboard_type(inline_keyboard): + raise ValueError( + "The parameter `inline_keyboard` should be a sequence of sequences of " + "InlineKeyboardButtons" + ) + # Required + self.inline_keyboard: Tuple[Tuple[InlineKeyboardButton, ...], ...] = tuple( + tuple(row) for row in inline_keyboard + ) + + self._id_attrs = (self.inline_keyboard,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["InlineKeyboardMarkup"]: + """See :meth:`telegram.TelegramObject.de_json`.""" + if not data: + return None + + keyboard = [] + for row in data["inline_keyboard"]: + tmp = [] + for col in row: + btn = InlineKeyboardButton.de_json(col, bot) + if btn: + tmp.append(btn) + keyboard.append(tmp) + + return cls(keyboard) + + @classmethod + def from_button(cls, button: InlineKeyboardButton, **kwargs: object) -> "InlineKeyboardMarkup": + """Shortcut for:: + + InlineKeyboardMarkup([[button]], **kwargs) + + Return an InlineKeyboardMarkup from a single InlineKeyboardButton + + Args: + button (:class:`telegram.InlineKeyboardButton`): The button to use in the markup + + """ + return cls([[button]], **kwargs) # type: ignore[arg-type] + + @classmethod + def from_row( + cls, button_row: Sequence[InlineKeyboardButton], **kwargs: object + ) -> "InlineKeyboardMarkup": + """Shortcut for:: + + InlineKeyboardMarkup([button_row], **kwargs) + + Return an InlineKeyboardMarkup from a single row of InlineKeyboardButtons + + Args: + button_row (Sequence[:class:`telegram.InlineKeyboardButton`]): The button to use + in the markup + + .. versionchanged:: 20.0 + |sequenceargs| + + """ + return cls([button_row], **kwargs) # type: ignore[arg-type] + + @classmethod + def from_column( + cls, button_column: Sequence[InlineKeyboardButton], **kwargs: object + ) -> "InlineKeyboardMarkup": + """Shortcut for:: + + InlineKeyboardMarkup([[button] for button in button_column], **kwargs) + + Return an InlineKeyboardMarkup from a single column of InlineKeyboardButtons + + Args: + button_column (Sequence[:class:`telegram.InlineKeyboardButton`]): The button to use + in the markup + + .. versionchanged:: 20.0 + |sequenceargs| + + """ + button_grid = [[button] for button in button_column] + return cls(button_grid, **kwargs) # type: ignore[arg-type] diff --git a/inlinequery.py b/inlinequery.py new file mode 100644 index 0000000000000000000000000000000000000000..ba29a8646fe889a8228728c0f18aef10dcbdd36a --- /dev/null +++ b/inlinequery.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python +# pylint: disable=too-many-arguments +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram InlineQuery.""" + +from typing import TYPE_CHECKING, Callable, Final, Optional, Sequence, Union + +from telegram import constants +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.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput + +if TYPE_CHECKING: + from telegram import Bot, InlineQueryResult + + +class InlineQuery(TelegramObject): + """ + This object represents an incoming inline query. When the user sends an empty query, your bot + could return some default or trending results. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`id` is equal. + + .. figure:: https://core.telegram.org/file/464001466/10e4a/r4FKyQ7gw5g.134366/f2\ + 606a53d683374703 + :align: center + + Inline queries on Telegram + + .. seealso:: + The :class:`telegram.InlineQueryResult` classes represent the media the user can choose + from (see above figure). + + Note: + In Python :keyword:`from` is a reserved word. Use :paramref:`from_user` instead. + + .. versionchanged:: 20.0 + The following are now keyword-only arguments in Bot methods: + ``{read, write, connect, pool}_timeout``, :paramref:`answer.api_kwargs`, + ``auto_pagination``. Use a named argument for those, + and notice that some positional arguments changed position as a result. + + Args: + id (:obj:`str`): Unique identifier for this query. + from_user (:class:`telegram.User`): Sender. + query (:obj:`str`): Text of the query (up to + :tg-const:`telegram.InlineQuery.MAX_QUERY_LENGTH` characters). + offset (:obj:`str`): Offset of the results to be returned, can be controlled by the bot. + chat_type (:obj:`str`, optional): Type of the chat, from which the inline query was sent. + Can be either :tg-const:`telegram.Chat.SENDER` for a private chat with the inline query + sender, :tg-const:`telegram.Chat.PRIVATE`, :tg-const:`telegram.Chat.GROUP`, + :tg-const:`telegram.Chat.SUPERGROUP` or :tg-const:`telegram.Chat.CHANNEL`. The chat + type should be always known for requests sent from official clients and most + third-party clients, unless the request was sent from a secret chat. + + .. versionadded:: 13.5 + location (:class:`telegram.Location`, optional): Sender location, only for bots that + request user location. + + Attributes: + id (:obj:`str`): Unique identifier for this query. + from_user (:class:`telegram.User`): Sender. + query (:obj:`str`): Text of the query (up to + :tg-const:`telegram.InlineQuery.MAX_QUERY_LENGTH` characters). + offset (:obj:`str`): Offset of the results to be returned, can be controlled by the bot. + chat_type (:obj:`str`): Optional. Type of the chat, from which the inline query was sent. + Can be either :tg-const:`telegram.Chat.SENDER` for a private chat with the inline query + sender, :tg-const:`telegram.Chat.PRIVATE`, :tg-const:`telegram.Chat.GROUP`, + :tg-const:`telegram.Chat.SUPERGROUP` or :tg-const:`telegram.Chat.CHANNEL`. The chat + type should be always known for requests sent from official clients and most + third-party clients, unless the request was sent from a secret chat. + + .. versionadded:: 13.5 + location (:class:`telegram.Location`): Optional. Sender location, only for bots that + request user location. + + """ + + __slots__ = ("chat_type", "from_user", "id", "location", "offset", "query") + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + from_user: User, + query: str, + offset: str, + location: Optional[Location] = None, + chat_type: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.id: str = id + self.from_user: User = from_user + self.query: str = query + self.offset: str = offset + + # Optional + self.location: Optional[Location] = location + self.chat_type: Optional[str] = chat_type + + self._id_attrs = (self.id,) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["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) + + return super().de_json(data=data, bot=bot) + + async def answer( + self, + results: Union[ + Sequence["InlineQueryResult"], Callable[[int], Optional[Sequence["InlineQueryResult"]]] + ], + cache_time: Optional[int] = None, + is_personal: Optional[bool] = None, + next_offset: Optional[str] = None, + button: Optional[InlineQueryResultsButton] = None, + *, + current_offset: Optional[str] = None, + auto_pagination: bool = False, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.answer_inline_query( + update.inline_query.id, + *args, + current_offset=self.offset if auto_pagination else None, + **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.answer_inline_query`. + + .. versionchanged:: 20.0 + Raises :class:`ValueError` instead of :class:`TypeError`. + + Keyword Args: + auto_pagination (:obj:`bool`, optional): If set to :obj:`True`, :attr:`offset` will be + passed as + :paramref:`current_offset ` to + :meth:`telegram.Bot.answer_inline_query`. + Defaults to :obj:`False`. + + Raises: + ValueError: If both :paramref:`~telegram.Bot.answer_inline_query.current_offset` and + :paramref:`auto_pagination` are supplied. + """ + if current_offset and auto_pagination: + raise ValueError("current_offset and auto_pagination are mutually exclusive!") + return await self.get_bot().answer_inline_query( + inline_query_id=self.id, + current_offset=self.offset if auto_pagination else current_offset, + results=results, + cache_time=cache_time, + is_personal=is_personal, + next_offset=next_offset, + button=button, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + MAX_RESULTS: Final[int] = constants.InlineQueryLimit.RESULTS + """:const:`telegram.constants.InlineQueryLimit.RESULTS` + + .. versionadded:: 13.2 + """ + MIN_SWITCH_PM_TEXT_LENGTH: Final[int] = constants.InlineQueryLimit.MIN_SWITCH_PM_TEXT_LENGTH + """:const:`telegram.constants.InlineQueryLimit.MIN_SWITCH_PM_TEXT_LENGTH` + + .. versionadded:: 20.0 + """ + MAX_SWITCH_PM_TEXT_LENGTH: Final[int] = constants.InlineQueryLimit.MAX_SWITCH_PM_TEXT_LENGTH + """:const:`telegram.constants.InlineQueryLimit.MAX_SWITCH_PM_TEXT_LENGTH` + + .. versionadded:: 20.0 + """ + MAX_OFFSET_LENGTH: Final[int] = constants.InlineQueryLimit.MAX_OFFSET_LENGTH + """:const:`telegram.constants.InlineQueryLimit.MAX_OFFSET_LENGTH` + + .. versionadded:: 20.0 + """ + MAX_QUERY_LENGTH: Final[int] = constants.InlineQueryLimit.MAX_QUERY_LENGTH + """:const:`telegram.constants.InlineQueryLimit.MAX_QUERY_LENGTH` + + .. versionadded:: 20.0 + """ diff --git a/inlinequeryresult.py b/inlinequeryresult.py new file mode 100644 index 0000000000000000000000000000000000000000..534d255c30578653ca8a507f34c60aa61cd2e835 --- /dev/null +++ b/inlinequeryresult.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=redefined-builtin +"""This module contains the classes that represent Telegram InlineQueryResult.""" + +from typing import Final, Optional + +from telegram import constants +from telegram._telegramobject import TelegramObject +from telegram._utils import enum +from telegram._utils.types import JSONDict + + +class InlineQueryResult(TelegramObject): + """Baseclass for the InlineQueryResult* classes. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`id` is equal. + + Note: + All URLs passed in inline query results will be available to end users and therefore must + be assumed to be *public*. + + Examples: + :any:`Inline Bot ` + + Args: + type (:obj:`str`): Type of the result. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + + Attributes: + type (:obj:`str`): Type of the result. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + + """ + + __slots__ = ("id", "type") + + def __init__(self, type: str, id: str, *, api_kwargs: Optional[JSONDict] = None): + super().__init__(api_kwargs=api_kwargs) + + # Required + self.type: str = enum.get_member(constants.InlineQueryResultType, type, type) + self.id: str = str(id) + + self._id_attrs = (self.id,) + + self._freeze() + + MIN_ID_LENGTH: Final[int] = constants.InlineQueryResultLimit.MIN_ID_LENGTH + """:const:`telegram.constants.InlineQueryResultLimit.MIN_ID_LENGTH` + + .. versionadded:: 20.0 + """ + MAX_ID_LENGTH: Final[int] = constants.InlineQueryResultLimit.MAX_ID_LENGTH + """:const:`telegram.constants.InlineQueryResultLimit.MAX_ID_LENGTH` + + .. versionadded:: 20.0 + """ diff --git a/inlinequeryresultarticle.py b/inlinequeryresultarticle.py new file mode 100644 index 0000000000000000000000000000000000000000..92c358e77efa99975a3ebbde2e419865c39d1d0d --- /dev/null +++ b/inlinequeryresultarticle.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultArticle.""" + +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.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultArticle(InlineQueryResult): + """This object represents a Telegram InlineQueryResultArticle. + + Examples: + :any:`Inline Bot ` + + .. versionchanged:: 20.5 + Removed the deprecated arguments and attributes ``thumb_*``. + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + title (:obj:`str`): Title of the result. + input_message_content (:class:`telegram.InputMessageContent`): Content of the message to + be sent. + 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. + description (:obj:`str`, optional): Short description of the result. + thumbnail_url (:obj:`str`, optional): Url of the thumbnail for the result. + + .. versionadded:: 20.2 + thumbnail_width (:obj:`int`, optional): Thumbnail width. + + .. versionadded:: 20.2 + thumbnail_height (:obj:`int`, optional): Thumbnail height. + + .. versionadded:: 20.2 + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.ARTICLE`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + title (:obj:`str`): Title of the result. + input_message_content (:class:`telegram.InputMessageContent`): Content of the message to + be sent. + 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. + description (:obj:`str`): Optional. Short description of the result. + thumbnail_url (:obj:`str`): Optional. Url of the thumbnail for the result. + + .. versionadded:: 20.2 + thumbnail_width (:obj:`int`): Optional. Thumbnail width. + + .. versionadded:: 20.2 + thumbnail_height (:obj:`int`): Optional. Thumbnail height. + + .. versionadded:: 20.2 + + """ + + __slots__ = ( + "description", + "hide_url", + "input_message_content", + "reply_markup", + "thumbnail_height", + "thumbnail_url", + "thumbnail_width", + "title", + "url", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + title: str, + 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, + thumbnail_height: Optional[int] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.ARTICLE, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.title: str = title + self.input_message_content: InputMessageContent = input_message_content + + # Optional + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.url: Optional[str] = url + 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 + self.thumbnail_height: Optional[int] = thumbnail_height diff --git a/inlinequeryresultaudio.py b/inlinequeryresultaudio.py new file mode 100644 index 0000000000000000000000000000000000000000..69353967adc0fc8b018a5bc8d19bbfef0a5e814b --- /dev/null +++ b/inlinequeryresultaudio.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultAudio.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput +from telegram.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultAudio(InlineQueryResult): + """ + Represents a link to an mp3 audio file. By default, this audio file will be sent by the user. + Alternatively, you can use :attr:`input_message_content` to send a message with the specified + content instead of the audio. + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + audio_url (:obj:`str`): A valid URL for the audio file. + title (:obj:`str`): Title. + performer (:obj:`str`, optional): Performer. + audio_duration (:obj:`str`, optional): Audio duration in seconds. + caption (:obj:`str`, optional): Caption, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities + parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + + .. versionchanged:: 20.0 + |sequenceclassargs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the audio. + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.AUDIO`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + audio_url (:obj:`str`): A valid URL for the audio file. + title (:obj:`str`): Title. + performer (:obj:`str`): Optional. Performer. + audio_duration (:obj:`str`): Optional. Audio duration in seconds. + caption (:obj:`str`): Optional. Caption, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities + parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the audio. + + """ + + __slots__ = ( + "audio_duration", + "audio_url", + "caption", + "caption_entities", + "input_message_content", + "parse_mode", + "performer", + "reply_markup", + "title", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + audio_url: str, + title: str, + performer: Optional[str] = None, + audio_duration: Optional[int] = None, + caption: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence[MessageEntity]] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.AUDIO, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.audio_url: str = audio_url + self.title: str = title + + # Optionals + self.performer: Optional[str] = performer + self.audio_duration: Optional[int] = audio_duration + self.caption: Optional[str] = caption + self.parse_mode: ODVInput[str] = parse_mode + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content diff --git a/inlinequeryresultcachedaudio.py b/inlinequeryresultcachedaudio.py new file mode 100644 index 0000000000000000000000000000000000000000..2fb7cdbb54d78bc8fb5cb2122e64d8a4b6096b87 --- /dev/null +++ b/inlinequeryresultcachedaudio.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultCachedAudio.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput +from telegram.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultCachedAudio(InlineQueryResult): + """ + Represents a link to an mp3 audio file stored on the Telegram servers. By default, this audio + file will be sent by the user. Alternatively, you can use :attr:`input_message_content` to + send a message with the specified content instead of the audio. + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + audio_file_id (:obj:`str`): A valid file identifier for the audio file. + caption (:obj:`str`, optional): Caption, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities + parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + + .. versionchanged:: 20.0 + |sequenceclassargs| + + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the audio. + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.AUDIO`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + audio_file_id (:obj:`str`): A valid file identifier for the audio file. + caption (:obj:`str`): Optional. Caption, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities + parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the audio. + + """ + + __slots__ = ( + "audio_file_id", + "caption", + "caption_entities", + "input_message_content", + "parse_mode", + "reply_markup", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + audio_file_id: str, + caption: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence[MessageEntity]] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.AUDIO, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.audio_file_id: str = audio_file_id + + # Optionals + self.caption: Optional[str] = caption + self.parse_mode: ODVInput[str] = parse_mode + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content diff --git a/inlinequeryresultcacheddocument.py b/inlinequeryresultcacheddocument.py new file mode 100644 index 0000000000000000000000000000000000000000..b5416c2748c4fdfa22dfb94240b084964f1e802b --- /dev/null +++ b/inlinequeryresultcacheddocument.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultCachedDocument.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput +from telegram.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultCachedDocument(InlineQueryResult): + """ + Represents a link to a file stored on the Telegram servers. By default, this file will be sent + by the user with an optional caption. Alternatively, you can use :attr:`input_message_content` + to send a message with the specified content instead of the file. + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + title (:obj:`str`): Title for the result. + document_file_id (:obj:`str`): A valid file identifier for the file. + description (:obj:`str`, optional): Short description of the result. + caption (:obj:`str`, optional): Caption of the document to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters + after entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + + .. versionchanged:: 20.0 + |sequenceclassargs| + + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the file. + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.DOCUMENT`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + title (:obj:`str`): Title for the result. + document_file_id (:obj:`str`): A valid file identifier for the file. + description (:obj:`str`): Optional. Short description of the result. + caption (:obj:`str`): Optional. Caption of the document to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters + after entities parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the file. + + """ + + __slots__ = ( + "caption", + "caption_entities", + "description", + "document_file_id", + "input_message_content", + "parse_mode", + "reply_markup", + "title", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + title: str, + document_file_id: str, + description: Optional[str] = None, + caption: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence[MessageEntity]] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.DOCUMENT, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.title: str = title + self.document_file_id: str = document_file_id + + # Optionals + self.description: Optional[str] = description + self.caption: Optional[str] = caption + self.parse_mode: ODVInput[str] = parse_mode + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content diff --git a/inlinequeryresultcachedgif.py b/inlinequeryresultcachedgif.py new file mode 100644 index 0000000000000000000000000000000000000000..9f52347a05c1c1a515b896bc611f899d58581895 --- /dev/null +++ b/inlinequeryresultcachedgif.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultCachedGif.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput +from telegram.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultCachedGif(InlineQueryResult): + """ + Represents a link to an animated GIF file stored on the Telegram servers. By default, this + animated GIF file will be sent by the user with an optional caption. Alternatively, you can + use :attr:`input_message_content` to send a message with specified content instead of + the animation. + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + gif_file_id (:obj:`str`): A valid file identifier for the GIF file. + title (:obj:`str`, optional): Title for the result. + caption (:obj:`str`, optional): Caption of the GIF file to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters + after entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + + .. versionchanged:: 20.0 + |sequenceclassargs| + + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the gif. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: 21.3 + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.GIF`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + gif_file_id (:obj:`str`): A valid file identifier for the GIF file. + title (:obj:`str`): Optional. Title for the result. + caption (:obj:`str`): Optional. Caption of the GIF file to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters + after entities parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the gif. + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: 21.3 + + """ + + __slots__ = ( + "caption", + "caption_entities", + "gif_file_id", + "input_message_content", + "parse_mode", + "reply_markup", + "show_caption_above_media", + "title", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + gif_file_id: str, + title: Optional[str] = None, + caption: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence[MessageEntity]] = None, + show_caption_above_media: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.GIF, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.gif_file_id: str = gif_file_id + + # Optionals + self.title: Optional[str] = title + self.caption: Optional[str] = caption + self.parse_mode: ODVInput[str] = parse_mode + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/inlinequeryresultcachedmpeg4gif.py b/inlinequeryresultcachedmpeg4gif.py new file mode 100644 index 0000000000000000000000000000000000000000..f750f4df8fdb2b6486ad69b21c8464a845a1eb44 --- /dev/null +++ b/inlinequeryresultcachedmpeg4gif.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput +from telegram.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): + """ + Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the + Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an + optional caption. Alternatively, you can use :attr:`input_message_content` to send a message + with the specified content instead of the animation. + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + mpeg4_file_id (:obj:`str`): A valid file identifier for the MP4 file. + title (:obj:`str`, optional): Title for the result. + caption (:obj:`str`, optional): Caption of the MPEG-4 file to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters + after entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + + .. versionchanged:: 20.0 + |sequenceclassargs| + + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the MPEG-4 file. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: 21.3 + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.MPEG4GIF`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + mpeg4_file_id (:obj:`str`): A valid file identifier for the MP4 file. + title (:obj:`str`): Optional. Title for the result. + caption (:obj:`str`): Optional. Caption of the MPEG-4 file to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters + after entities parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the MPEG-4 file. + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: 21.3 + + """ + + __slots__ = ( + "caption", + "caption_entities", + "input_message_content", + "mpeg4_file_id", + "parse_mode", + "reply_markup", + "show_caption_above_media", + "title", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + mpeg4_file_id: str, + title: Optional[str] = None, + caption: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence[MessageEntity]] = None, + show_caption_above_media: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.MPEG4GIF, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.mpeg4_file_id: str = mpeg4_file_id + + # Optionals + self.title: Optional[str] = title + self.caption: Optional[str] = caption + self.parse_mode: ODVInput[str] = parse_mode + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/inlinequeryresultcachedphoto.py b/inlinequeryresultcachedphoto.py new file mode 100644 index 0000000000000000000000000000000000000000..75f292d2e32e14c34c76b5785c852d241cfd518f --- /dev/null +++ b/inlinequeryresultcachedphoto.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultPhoto""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput +from telegram.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultCachedPhoto(InlineQueryResult): + """ + Represents a link to a photo stored on the Telegram servers. By default, this photo will be + sent by the user with an optional caption. Alternatively, you can use + :attr:`input_message_content` to send a message with the specified content instead + of the photo. + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + photo_file_id (:obj:`str`): A valid file identifier of the photo. + title (:obj:`str`, optional): Title for the result. + description (:obj:`str`, optional): Short description of the result. + caption (:obj:`str`, optional): Caption of the photo to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after + entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + + .. versionchanged:: 20.0 + |sequenceclassargs| + + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the photo. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: 21.3 + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.PHOTO`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + photo_file_id (:obj:`str`): A valid file identifier of the photo. + title (:obj:`str`): Optional. Title for the result. + description (:obj:`str`): Optional. Short description of the result. + caption (:obj:`str`): Optional. Caption of the photo to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after + entities parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the photo. + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: 21.3 + + """ + + __slots__ = ( + "caption", + "caption_entities", + "description", + "input_message_content", + "parse_mode", + "photo_file_id", + "reply_markup", + "show_caption_above_media", + "title", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + photo_file_id: str, + title: Optional[str] = None, + description: Optional[str] = None, + caption: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence[MessageEntity]] = None, + show_caption_above_media: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.PHOTO, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.photo_file_id: str = photo_file_id + + # Optionals + self.title: Optional[str] = title + self.description: Optional[str] = description + self.caption: Optional[str] = caption + self.parse_mode: ODVInput[str] = parse_mode + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/inlinequeryresultcachedsticker.py b/inlinequeryresultcachedsticker.py new file mode 100644 index 0000000000000000000000000000000000000000..8e8d22544ca3336588485a4247b23d7a0902721e --- /dev/null +++ b/inlinequeryresultcachedsticker.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultCachedSticker.""" + +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.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultCachedSticker(InlineQueryResult): + """ + Represents a link to a sticker stored on the Telegram servers. By default, this sticker will + be sent by the user. Alternatively, you can use :attr:`input_message_content` to send a + message with the specified content instead of the sticker. + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + sticker_file_id (:obj:`str`): A valid file identifier of the sticker. + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the sticker. + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.STICKER`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + sticker_file_id (:obj:`str`): A valid file identifier of the sticker. + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the sticker. + + """ + + __slots__ = ("input_message_content", "reply_markup", "sticker_file_id") + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + sticker_file_id: str, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.STICKER, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.sticker_file_id: str = sticker_file_id + + # Optionals + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content diff --git a/inlinequeryresultcachedvideo.py b/inlinequeryresultcachedvideo.py new file mode 100644 index 0000000000000000000000000000000000000000..99a58eebbe566f22cef38a2dd881e00c261b7852 --- /dev/null +++ b/inlinequeryresultcachedvideo.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultCachedVideo.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput +from telegram.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultCachedVideo(InlineQueryResult): + """ + Represents a link to a video file stored on the Telegram servers. By default, this video file + will be sent by the user with an optional caption. Alternatively, you can use + :attr:`input_message_content` to send a message with the specified content instead + of the video. + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + video_file_id (:obj:`str`): A valid file identifier for the video file. + title (:obj:`str`): Title for the result. + description (:obj:`str`, optional): Short description of the result. + caption (:obj:`str`, optional): Caption of the video to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after + entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the video. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: 21.3 + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VIDEO`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + video_file_id (:obj:`str`): A valid file identifier for the video file. + title (:obj:`str`): Title for the result. + description (:obj:`str`): Optional. Short description of the result. + caption (:obj:`str`): Optional. Caption of the video to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after + entities parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the video. + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: 21.3 + + """ + + __slots__ = ( + "caption", + "caption_entities", + "description", + "input_message_content", + "parse_mode", + "reply_markup", + "show_caption_above_media", + "title", + "video_file_id", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + video_file_id: str, + title: str, + description: Optional[str] = None, + caption: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence[MessageEntity]] = None, + show_caption_above_media: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.VIDEO, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.video_file_id: str = video_file_id + self.title: str = title + + # Optionals + self.description: Optional[str] = description + self.caption: Optional[str] = caption + self.parse_mode: ODVInput[str] = parse_mode + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/inlinequeryresultcachedvoice.py b/inlinequeryresultcachedvoice.py new file mode 100644 index 0000000000000000000000000000000000000000..dc8bd2ad3a6bf6abbd0cfc4a38fc4899fa16f9dc --- /dev/null +++ b/inlinequeryresultcachedvoice.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultCachedVoice.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput +from telegram.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultCachedVoice(InlineQueryResult): + """ + Represents a link to a voice message stored on the Telegram servers. By default, this voice + message will be sent by the user. Alternatively, you can use :attr:`input_message_content` to + send a message with the specified content instead of the voice message. + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + voice_file_id (:obj:`str`): A valid file identifier for the voice message. + title (:obj:`str`): Voice message title. + caption (:obj:`str`, optional): Caption, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities + parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): + |captionentitiesattr| + + .. versionchanged:: 20.0 + |sequenceclassargs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the voice message. + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VOICE`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + voice_file_id (:obj:`str`): A valid file identifier for the voice message. + title (:obj:`str`): Voice message title. + caption (:obj:`str`): Optional. Caption, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities + parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the voice message. + + """ + + __slots__ = ( + "caption", + "caption_entities", + "input_message_content", + "parse_mode", + "reply_markup", + "title", + "voice_file_id", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + voice_file_id: str, + title: str, + caption: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence[MessageEntity]] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.VOICE, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.voice_file_id: str = voice_file_id + self.title: str = title + + # Optionals + self.caption: Optional[str] = caption + self.parse_mode: ODVInput[str] = parse_mode + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content diff --git a/inlinequeryresultcontact.py b/inlinequeryresultcontact.py new file mode 100644 index 0000000000000000000000000000000000000000..faff47454d32c53f5cf450066d677cff804a8d89 --- /dev/null +++ b/inlinequeryresultcontact.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultContact.""" + +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.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultContact(InlineQueryResult): + """ + Represents a contact with a phone number. By default, this contact will be sent by the user. + Alternatively, you can use :attr:`input_message_content` to send a message with the specified + content instead of the contact. + + .. versionchanged:: 20.5 + |removed_thumb_wildcard_note| + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + phone_number (:obj:`str`): Contact's phone number. + first_name (:obj:`str`): Contact's first name. + last_name (:obj:`str`, optional): Contact's last name. + vcard (:obj:`str`, optional): Additional data about the contact in the form of a vCard, + 0-:tg-const:`telegram.constants.ContactLimit.VCARD` bytes. + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the contact. + thumbnail_url (:obj:`str`, optional): Url of the thumbnail for the result. + + .. versionadded:: 20.2 + thumbnail_width (:obj:`int`, optional): Thumbnail width. + + .. versionadded:: 20.2 + thumbnail_height (:obj:`int`, optional): Thumbnail height. + + .. versionadded:: 20.2 + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.CONTACT`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + phone_number (:obj:`str`): Contact's phone number. + first_name (:obj:`str`): Contact's first name. + last_name (:obj:`str`): Optional. Contact's last name. + vcard (:obj:`str`): Optional. Additional data about the contact in the form of a vCard, + 0-:tg-const:`telegram.constants.ContactLimit.VCARD` bytes. + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the contact. + thumbnail_url (:obj:`str`): Optional. Url of the thumbnail for the result. + + .. versionadded:: 20.2 + thumbnail_width (:obj:`int`): Optional. Thumbnail width. + + .. versionadded:: 20.2 + thumbnail_height (:obj:`int`): Optional. Thumbnail height. + + .. versionadded:: 20.2 + + """ + + __slots__ = ( + "first_name", + "input_message_content", + "last_name", + "phone_number", + "reply_markup", + "thumbnail_height", + "thumbnail_url", + "thumbnail_width", + "vcard", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + phone_number: str, + first_name: str, + last_name: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + vcard: Optional[str] = None, + thumbnail_url: Optional[str] = None, + thumbnail_width: Optional[int] = None, + thumbnail_height: Optional[int] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.CONTACT, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.phone_number: str = phone_number + self.first_name: str = first_name + + # Optionals + self.last_name: Optional[str] = last_name + self.vcard: Optional[str] = vcard + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.thumbnail_url: Optional[str] = thumbnail_url + self.thumbnail_width: Optional[int] = thumbnail_width + self.thumbnail_height: Optional[int] = thumbnail_height diff --git a/inlinequeryresultdocument.py b/inlinequeryresultdocument.py new file mode 100644 index 0000000000000000000000000000000000000000..e0380440b20a3388b8367d0d335260b03ee555e2 --- /dev/null +++ b/inlinequeryresultdocument.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultDocument""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput +from telegram.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultDocument(InlineQueryResult): + """ + Represents a link to a file. By default, this file will be sent by the user with an optional + caption. Alternatively, you can use :attr:`input_message_content` to send a message with the + specified content instead of the file. Currently, only .PDF and .ZIP files can be sent + using this method. + + .. seealso:: :wiki:`Working with Files and Media ` + + .. versionchanged:: 20.5 + |removed_thumb_wildcard_note| + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + title (:obj:`str`): Title for the result. + caption (:obj:`str`, optional): Caption of the document to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters + after entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + + .. versionchanged:: 20.0 + |sequenceclassargs| + + document_url (:obj:`str`): A valid URL for the file. + mime_type (:obj:`str`): Mime type of the content of the file, either "application/pdf" + or "application/zip". + description (:obj:`str`, optional): Short description of the result. + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the file. + thumbnail_url (:obj:`str`, optional): URL of the thumbnail (JPEG only) for the file. + + .. versionadded:: 20.2 + thumbnail_width (:obj:`int`, optional): Thumbnail width. + + .. versionadded:: 20.2 + thumbnail_height (:obj:`int`, optional): Thumbnail height. + + .. versionadded:: 20.2 + + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.DOCUMENT`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + title (:obj:`str`): Title for the result. + caption (:obj:`str`): Optional. Caption of the document to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters + after entities parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + document_url (:obj:`str`): A valid URL for the file. + mime_type (:obj:`str`): Mime type of the content of the file, either "application/pdf" + or "application/zip". + description (:obj:`str`): Optional. Short description of the result. + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the file. + thumbnail_url (:obj:`str`): Optional. URL of the thumbnail (JPEG only) for the file. + + .. versionadded:: 20.2 + thumbnail_width (:obj:`int`): Optional. Thumbnail width. + + .. versionadded:: 20.2 + thumbnail_height (:obj:`int`): Optional. Thumbnail height. + + .. versionadded:: 20.2 + + """ + + __slots__ = ( + "caption", + "caption_entities", + "description", + "document_url", + "input_message_content", + "mime_type", + "parse_mode", + "reply_markup", + "thumbnail_height", + "thumbnail_url", + "thumbnail_width", + "title", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + document_url: str, + title: str, + mime_type: str, + caption: Optional[str] = None, + description: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence[MessageEntity]] = None, + thumbnail_url: Optional[str] = None, + thumbnail_width: Optional[int] = None, + thumbnail_height: Optional[int] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.DOCUMENT, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.document_url: str = document_url + self.title: str = title + self.mime_type: str = mime_type + + # Optionals + self.caption: Optional[str] = caption + self.parse_mode: ODVInput[str] = parse_mode + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.description: Optional[str] = description + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.thumbnail_url: Optional[str] = thumbnail_url + self.thumbnail_width: Optional[int] = thumbnail_width + self.thumbnail_height: Optional[int] = thumbnail_height diff --git a/inlinequeryresultgame.py b/inlinequeryresultgame.py new file mode 100644 index 0000000000000000000000000000000000000000..aeb78c0f1b4ccbe7c6928c8a35839ec5b173f274 --- /dev/null +++ b/inlinequeryresultgame.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultGame.""" +from typing import Optional + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._utils.types import JSONDict +from telegram.constants import InlineQueryResultType + + +class InlineQueryResultGame(InlineQueryResult): + """Represents a :class:`telegram.Game`. + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + game_short_name (:obj:`str`): Short name of the game. + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.GAME`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + game_short_name (:obj:`str`): Short name of the game. + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + + """ + + __slots__ = ("game_short_name", "reply_markup") + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + game_short_name: str, + reply_markup: Optional[InlineKeyboardMarkup] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.GAME, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.id: str = id + self.game_short_name: str = game_short_name + + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup diff --git a/inlinequeryresultgif.py b/inlinequeryresultgif.py new file mode 100644 index 0000000000000000000000000000000000000000..e5694e4f856f8e04f8f3947dfb68b0dece1f77cb --- /dev/null +++ b/inlinequeryresultgif.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultGif.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput +from telegram.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultGif(InlineQueryResult): + """ + Represents a link to an animated GIF file. By default, this animated GIF file will be sent by + the user with optional caption. Alternatively, you can use :attr:`input_message_content` to + send a message with the specified content instead of the animation. + + .. seealso:: :wiki:`Working with Files and Media ` + + .. versionchanged:: 20.5 + |removed_thumb_wildcard_note| + + Args: + 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_width (:obj:`int`, optional): Width of the GIF. + gif_height (:obj:`int`, optional): Height of the GIF. + gif_duration (:obj:`int`, optional): Duration of the GIF in seconds. + thumbnail_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4) + thumbnail for the result. + + .. versionadded:: 20.2 + + ..versionchanged:: 20.5 + |thumbnail_url_mandatory| + + thumbnail_mime_type (:obj:`str`, optional): MIME type of the thumbnail, must be one of + ``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``. + + .. versionadded:: 20.2 + title (:obj:`str`, optional): Title for the result. + caption (:obj:`str`, optional): Caption of the GIF file to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters + after entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + + .. versionchanged:: 20.0 + |sequenceclassargs| + + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the GIF animation. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: 21.3 + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.GIF`. + 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_width (:obj:`int`): Optional. Width of the GIF. + gif_height (:obj:`int`): Optional. Height of the GIF. + gif_duration (:obj:`int`): Optional. Duration of the GIF in seconds. + thumbnail_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail + for the result. + + .. versionadded:: 20.2 + thumbnail_mime_type (:obj:`str`): Optional. MIME type of the thumbnail, must be one of + ``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``. + + .. versionadded:: 20.2 + title (:obj:`str`): Optional. Title for the result. + caption (:obj:`str`): Optional. Caption of the GIF file to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters + after entities parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the GIF animation. + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: 21.3 + + """ + + __slots__ = ( + "caption", + "caption_entities", + "gif_duration", + "gif_height", + "gif_url", + "gif_width", + "input_message_content", + "parse_mode", + "reply_markup", + "show_caption_above_media", + "thumbnail_mime_type", + "thumbnail_url", + "title", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + gif_url: str, + thumbnail_url: str, + gif_width: Optional[int] = None, + gif_height: Optional[int] = None, + title: Optional[str] = None, + caption: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + gif_duration: Optional[int] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence[MessageEntity]] = None, + thumbnail_mime_type: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.GIF, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.gif_url: str = gif_url + self.thumbnail_url: str = thumbnail_url + + # Optionals + self.gif_width: Optional[int] = gif_width + self.gif_height: Optional[int] = gif_height + self.gif_duration: Optional[int] = gif_duration + self.title: Optional[str] = title + self.caption: Optional[str] = caption + self.parse_mode: ODVInput[str] = parse_mode + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.thumbnail_mime_type: Optional[str] = thumbnail_mime_type + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/inlinequeryresultlocation.py b/inlinequeryresultlocation.py new file mode 100644 index 0000000000000000000000000000000000000000..dff2b29a48bed95e778de09ef0a2049482dbd088 --- /dev/null +++ b/inlinequeryresultlocation.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultLocation.""" + +from typing import TYPE_CHECKING, Final, Optional + +from telegram import constants +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultLocation(InlineQueryResult): + """ + Represents a location on a map. By default, the location will be sent by the user. + Alternatively, you can use :attr:`input_message_content` to send a message with the specified + content instead of the location. + + .. versionchanged:: 20.5 + |removed_thumb_wildcard_note| + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + latitude (:obj:`float`): Location latitude in degrees. + longitude (:obj:`float`): Location longitude in degrees. + title (:obj:`str`): Location title. + horizontal_accuracy (:obj:`float`, optional): The radius of uncertainty for the location, + measured in meters; 0- + :tg-const:`telegram.InlineQueryResultLocation.HORIZONTAL_ACCURACY`. + live_period (:obj:`int`, optional): Period in seconds for which the location will be + updated, should be between + :tg-const:`telegram.InlineQueryResultLocation.MIN_LIVE_PERIOD` and + :tg-const:`telegram.InlineQueryResultLocation.MAX_LIVE_PERIOD`. + heading (:obj:`int`, optional): For live locations, a direction in which the user is + moving, in degrees. Must be between + :tg-const:`telegram.InlineQueryResultLocation.MIN_HEADING` and + :tg-const:`telegram.InlineQueryResultLocation.MAX_HEADING` if specified. + proximity_alert_radius (:obj:`int`, optional): For live locations, a maximum distance + for proximity alerts about approaching another chat member, in meters. Must be + between :tg-const:`telegram.InlineQueryResultLocation.MIN_PROXIMITY_ALERT_RADIUS` + and :tg-const:`telegram.InlineQueryResultLocation.MAX_PROXIMITY_ALERT_RADIUS` + if specified. + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the location. + thumbnail_url (:obj:`str`, optional): Url of the thumbnail for the result. + + .. versionadded:: 20.2 + thumbnail_width (:obj:`int`, optional): Thumbnail width. + + .. versionadded:: 20.2 + thumbnail_height (:obj:`int`, optional): Thumbnail height. + + .. versionadded:: 20.2 + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.LOCATION`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + latitude (:obj:`float`): Location latitude in degrees. + longitude (:obj:`float`): Location longitude in degrees. + title (:obj:`str`): Location title. + horizontal_accuracy (:obj:`float`): Optional. The radius of uncertainty for the location, + measured in meters; 0- + :tg-const:`telegram.InlineQueryResultLocation.HORIZONTAL_ACCURACY`. + live_period (:obj:`int`): Optional. Period in seconds for which the location will be + updated, should be between + :tg-const:`telegram.InlineQueryResultLocation.MIN_LIVE_PERIOD` and + :tg-const:`telegram.InlineQueryResultLocation.MAX_LIVE_PERIOD` or + :tg-const:`telegram.constants.LocationLimit.LIVE_PERIOD_FOREVER` for live + locations that can be edited indefinitely. + heading (:obj:`int`): Optional. For live locations, a direction in which the user is + moving, in degrees. Must be between + :tg-const:`telegram.InlineQueryResultLocation.MIN_HEADING` and + :tg-const:`telegram.InlineQueryResultLocation.MAX_HEADING` if specified. + proximity_alert_radius (:obj:`int`): Optional. For live locations, a maximum distance + for proximity alerts about approaching another chat member, in meters. Must be + between :tg-const:`telegram.InlineQueryResultLocation.MIN_PROXIMITY_ALERT_RADIUS` + and :tg-const:`telegram.InlineQueryResultLocation.MAX_PROXIMITY_ALERT_RADIUS` + if specified. + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the location. + thumbnail_url (:obj:`str`): Optional. Url of the thumbnail for the result. + + .. versionadded:: 20.2 + thumbnail_width (:obj:`int`): Optional. Thumbnail width. + + .. versionadded:: 20.2 + thumbnail_height (:obj:`int`): Optional. Thumbnail height. + + .. versionadded:: 20.2 + + """ + + __slots__ = ( + "heading", + "horizontal_accuracy", + "input_message_content", + "latitude", + "live_period", + "longitude", + "proximity_alert_radius", + "reply_markup", + "thumbnail_height", + "thumbnail_url", + "thumbnail_width", + "title", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + latitude: float, + longitude: float, + title: str, + live_period: Optional[int] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + horizontal_accuracy: Optional[float] = None, + heading: Optional[int] = None, + proximity_alert_radius: Optional[int] = None, + thumbnail_url: Optional[str] = None, + thumbnail_width: Optional[int] = None, + thumbnail_height: Optional[int] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(constants.InlineQueryResultType.LOCATION, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.latitude: float = latitude + self.longitude: float = longitude + self.title: str = title + + # Optionals + self.live_period: Optional[int] = live_period + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.thumbnail_url: Optional[str] = thumbnail_url + self.thumbnail_width: Optional[int] = thumbnail_width + self.thumbnail_height: Optional[int] = thumbnail_height + self.horizontal_accuracy: Optional[float] = horizontal_accuracy + self.heading: Optional[int] = heading + self.proximity_alert_radius: Optional[int] = ( + int(proximity_alert_radius) if proximity_alert_radius else None + ) + + HORIZONTAL_ACCURACY: Final[int] = constants.LocationLimit.HORIZONTAL_ACCURACY + """:const:`telegram.constants.LocationLimit.HORIZONTAL_ACCURACY` + + .. versionadded:: 20.0 + """ + MIN_HEADING: Final[int] = constants.LocationLimit.MIN_HEADING + """:const:`telegram.constants.LocationLimit.MIN_HEADING` + + .. versionadded:: 20.0 + """ + MAX_HEADING: Final[int] = constants.LocationLimit.MAX_HEADING + """:const:`telegram.constants.LocationLimit.MAX_HEADING` + + .. versionadded:: 20.0 + """ + MIN_LIVE_PERIOD: Final[int] = constants.LocationLimit.MIN_LIVE_PERIOD + """:const:`telegram.constants.LocationLimit.MIN_LIVE_PERIOD` + + .. versionadded:: 20.0 + """ + MAX_LIVE_PERIOD: Final[int] = constants.LocationLimit.MAX_LIVE_PERIOD + """:const:`telegram.constants.LocationLimit.MAX_LIVE_PERIOD` + + .. versionadded:: 20.0 + """ + MIN_PROXIMITY_ALERT_RADIUS: Final[int] = constants.LocationLimit.MIN_PROXIMITY_ALERT_RADIUS + """:const:`telegram.constants.LocationLimit.MIN_PROXIMITY_ALERT_RADIUS` + + .. versionadded:: 20.0 + """ + MAX_PROXIMITY_ALERT_RADIUS: Final[int] = constants.LocationLimit.MAX_PROXIMITY_ALERT_RADIUS + """:const:`telegram.constants.LocationLimit.MAX_PROXIMITY_ALERT_RADIUS` + + .. versionadded:: 20.0 + """ diff --git a/inlinequeryresultmpeg4gif.py b/inlinequeryresultmpeg4gif.py new file mode 100644 index 0000000000000000000000000000000000000000..9e27ab949df81f44939fd82a3c6ee1b225f20023 --- /dev/null +++ b/inlinequeryresultmpeg4gif.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput +from telegram.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultMpeg4Gif(InlineQueryResult): + """ + Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default, this + animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you can + use :attr:`input_message_content` to send a message with the specified content instead of the + animation. + + .. seealso:: :wiki:`Working with Files and Media ` + + .. versionchanged:: 20.5 + |removed_thumb_wildcard_note| + + Args: + 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_width (:obj:`int`, optional): Video width. + mpeg4_height (:obj:`int`, optional): Video height. + mpeg4_duration (:obj:`int`, optional): Video duration in seconds. + thumbnail_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4) + thumbnail for the result. + + .. versionadded:: 20.2 + + ..versionchanged:: 20.5 + |thumbnail_url_mandatory| + + thumbnail_mime_type (:obj:`str`, optional): MIME type of the thumbnail, must be one of + ``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``. + + .. versionadded:: 20.2 + title (:obj:`str`, optional): Title for the result. + caption (:obj:`str`, optional): Caption of the MPEG-4 file to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters + after entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): + |captionentitiesattr| + + .. versionchanged:: 20.0 + |sequenceclassargs| + + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the video animation. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: 21.3 + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.MPEG4GIF`. + 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_width (:obj:`int`): Optional. Video width. + mpeg4_height (:obj:`int`): Optional. Video height. + mpeg4_duration (:obj:`int`): Optional. Video duration in seconds. + thumbnail_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail + for the result. + + .. versionadded:: 20.2 + thumbnail_mime_type (:obj:`str`): Optional. MIME type of the thumbnail, must be one of + ``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``. + + .. versionadded:: 20.2 + title (:obj:`str`): Optional. Title for the result. + caption (:obj:`str`): Optional. Caption of the MPEG-4 file to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters + after entities parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the video animation. + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: 21.3 + """ + + __slots__ = ( + "caption", + "caption_entities", + "input_message_content", + "mpeg4_duration", + "mpeg4_height", + "mpeg4_url", + "mpeg4_width", + "parse_mode", + "reply_markup", + "show_caption_above_media", + "thumbnail_mime_type", + "thumbnail_url", + "title", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + mpeg4_url: str, + thumbnail_url: str, + mpeg4_width: Optional[int] = None, + mpeg4_height: Optional[int] = None, + title: Optional[str] = None, + caption: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + mpeg4_duration: Optional[int] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence[MessageEntity]] = None, + thumbnail_mime_type: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.MPEG4GIF, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.mpeg4_url: str = mpeg4_url + self.thumbnail_url: str = thumbnail_url + + # Optional + self.mpeg4_width: Optional[int] = mpeg4_width + self.mpeg4_height: Optional[int] = mpeg4_height + self.mpeg4_duration: Optional[int] = mpeg4_duration + self.title: Optional[str] = title + self.caption: Optional[str] = caption + self.parse_mode: ODVInput[str] = parse_mode + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.thumbnail_mime_type: Optional[str] = thumbnail_mime_type + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/inlinequeryresultphoto.py b/inlinequeryresultphoto.py new file mode 100644 index 0000000000000000000000000000000000000000..b74adf218e382f0e43708a85a3b9a4dc56074e85 --- /dev/null +++ b/inlinequeryresultphoto.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultPhoto.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput +from telegram.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultPhoto(InlineQueryResult): + """ + Represents a link to a photo. By default, this photo will be sent by the user with optional + caption. Alternatively, you can use :attr:`input_message_content` to send a message with the + specified content instead of the photo. + + .. seealso:: :wiki:`Working with Files and Media ` + + .. versionchanged:: 20.5 + |removed_thumb_url_note| + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + photo_url (:obj:`str`): A valid URL of the photo. Photo must be in JPEG format. Photo size + must not exceed 5MB. + thumbnail_url (:obj:`str`): URL of the thumbnail for the photo. + + .. versionadded:: 20.2 + + ..versionchanged:: 20.5 + |thumbnail_url_mandatory| + + photo_width (:obj:`int`, optional): Width of the photo. + photo_height (:obj:`int`, optional): Height of the photo. + title (:obj:`str`, optional): Title for the result. + description (:obj:`str`, optional): Short description of the result. + caption (:obj:`str`, optional): Caption of the photo to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after + entities parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + + .. versionchanged:: 20.0 + |sequenceclassargs| + + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the photo. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: 21.3 + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.PHOTO`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + photo_url (:obj:`str`): A valid URL of the photo. Photo must be in JPEG format. Photo size + must not exceed 5MB. + thumbnail_url (:obj:`str`): URL of the thumbnail for the photo. + photo_width (:obj:`int`): Optional. Width of the photo. + photo_height (:obj:`int`): Optional. Height of the photo. + title (:obj:`str`): Optional. Title for the result. + description (:obj:`str`): Optional. Short description of the result. + caption (:obj:`str`): Optional. Caption of the photo to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after + entities parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the photo. + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: 21.3 + + """ + + __slots__ = ( + "caption", + "caption_entities", + "description", + "input_message_content", + "parse_mode", + "photo_height", + "photo_url", + "photo_width", + "reply_markup", + "show_caption_above_media", + "thumbnail_url", + "title", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + photo_url: str, + thumbnail_url: str, + photo_width: Optional[int] = None, + photo_height: Optional[int] = None, + title: Optional[str] = None, + description: Optional[str] = None, + caption: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence[MessageEntity]] = None, + show_caption_above_media: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.PHOTO, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.photo_url: str = photo_url + self.thumbnail_url: str = thumbnail_url + + # Optionals + self.photo_width: Optional[int] = photo_width + self.photo_height: Optional[int] = photo_height + self.title: Optional[str] = title + self.description: Optional[str] = description + self.caption: Optional[str] = caption + self.parse_mode: ODVInput[str] = parse_mode + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/inlinequeryresultsbutton.py b/inlinequeryresultsbutton.py new file mode 100644 index 0000000000000000000000000000000000000000..ae0b404e1f8b6e7a9e1c64ebee55206dfe4fde04 --- /dev/null +++ b/inlinequeryresultsbutton.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the class that represent a Telegram InlineQueryResultsButton.""" + +from typing import TYPE_CHECKING, Final, Optional + +from telegram import constants +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict +from telegram._webappinfo import WebAppInfo + +if TYPE_CHECKING: + from telegram import Bot + + +class InlineQueryResultsButton(TelegramObject): + """This object represents a button to be shown above inline query results. You **must** use + exactly one of the optional fields. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`text`, :attr:`web_app` and :attr:`start_parameter` are equal. + + Args: + text (:obj:`str`): Label text on the button. + web_app (:class:`telegram.WebAppInfo`, optional): Description of the + `Web App `_ that will be launched when the + user presses the button. The Web App will be able to switch back to the inline mode + using the method + `switchInlineQuery `_ + inside the Web App. + start_parameter (:obj:`str`, optional): Deep-linking parameter for the + :guilabel:`/start` message sent to the bot when user presses the switch button. + :tg-const:`telegram.InlineQuery.MIN_SWITCH_PM_TEXT_LENGTH`- + :tg-const:`telegram.InlineQuery.MAX_SWITCH_PM_TEXT_LENGTH` characters, + only ``A-Z``, ``a-z``, ``0-9``, ``_`` and ``-`` are allowed. + + Example: + An inline bot that sends YouTube videos can ask the user to connect the bot to + their YouTube account to adapt search results accordingly. To do this, it displays + a 'Connect your YouTube account' button above the results, or even before showing + any. The user presses the button, switches to a private chat with the bot and, in + doing so, passes a start parameter that instructs the bot to return an OAuth link. + Once done, the bot can offer a switch_inline button so that the user can easily + return to the chat where they wanted to use the bot's inline capabilities. + + Attributes: + text (:obj:`str`): Label text on the button. + web_app (:class:`telegram.WebAppInfo`): Optional. Description of the + `Web App `_ that will be launched when the + user presses the button. The Web App will be able to switch back to the inline mode + using the method ``web_app_switch_inline_query`` inside the Web App. + start_parameter (:obj:`str`): Optional. Deep-linking parameter for the + :guilabel:`/start` message sent to the bot when user presses the switch button. + :tg-const:`telegram.InlineQuery.MIN_SWITCH_PM_TEXT_LENGTH`- + :tg-const:`telegram.InlineQuery.MAX_SWITCH_PM_TEXT_LENGTH` characters, + only ``A-Z``, ``a-z``, ``0-9``, ``_`` and ``-`` are allowed. + + """ + + __slots__ = ("start_parameter", "text", "web_app") + + def __init__( + self, + text: str, + web_app: Optional[WebAppInfo] = None, + start_parameter: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + # Required + self.text: str = text + + # Optional + self.web_app: Optional[WebAppInfo] = web_app + self.start_parameter: Optional[str] = start_parameter + + self._id_attrs = (self.text, self.web_app, self.start_parameter) + + self._freeze() + + @classmethod + def de_json( + cls, data: Optional[JSONDict], bot: Optional["Bot"] = None + ) -> Optional["InlineQueryResultsButton"]: + """See :meth:`telegram.TelegramObject.de_json`.""" + if not data: + return None + + data["web_app"] = WebAppInfo.de_json(data.get("web_app"), bot) + + return super().de_json(data=data, bot=bot) + + MIN_START_PARAMETER_LENGTH: Final[int] = ( + constants.InlineQueryResultsButtonLimit.MIN_START_PARAMETER_LENGTH + ) + """:const:`telegram.constants.InlineQueryResultsButtonLimit.MIN_START_PARAMETER_LENGTH`""" + MAX_START_PARAMETER_LENGTH: Final[int] = ( + constants.InlineQueryResultsButtonLimit.MAX_START_PARAMETER_LENGTH + ) + """:const:`telegram.constants.InlineQueryResultsButtonLimit.MAX_START_PARAMETER_LENGTH`""" diff --git a/inlinequeryresultvenue.py b/inlinequeryresultvenue.py new file mode 100644 index 0000000000000000000000000000000000000000..60af4024f86e4c88256a889815bbb07ff2efde57 --- /dev/null +++ b/inlinequeryresultvenue.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultVenue.""" + +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.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultVenue(InlineQueryResult): + """ + Represents a venue. By default, the venue will be sent by the user. Alternatively, you can + use :attr:`input_message_content` to send a message with the specified content instead of the + venue. + + Note: + Foursquare details and Google Pace details are mutually exclusive. However, this + behaviour is undocumented and might be changed by Telegram. + + .. versionchanged:: 20.5 + |removed_thumb_wildcard_note| + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + latitude (:obj:`float`): Latitude of the venue location in degrees. + longitude (:obj:`float`): Longitude of the venue location in degrees. + title (:obj:`str`): Title of the venue. + address (:obj:`str`): Address of the venue. + foursquare_id (:obj:`str`, optional): Foursquare identifier of the venue if known. + foursquare_type (:obj:`str`, optional): Foursquare type of the venue, if known. + (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or + "food/icecream".) + google_place_id (:obj:`str`, optional): Google Places identifier of the venue. + google_place_type (:obj:`str`, optional): Google Places type of the venue. (See + `supported types `_.) + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the venue. + thumbnail_url (:obj:`str`, optional): Url of the thumbnail for the result. + + .. versionadded:: 20.2 + thumbnail_width (:obj:`int`, optional): Thumbnail width. + + .. versionadded:: 20.2 + thumbnail_height (:obj:`int`, optional): Thumbnail height. + + .. versionadded:: 20.2 + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VENUE`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + latitude (:obj:`float`): Latitude of the venue location in degrees. + longitude (:obj:`float`): Longitude of the venue location in degrees. + title (:obj:`str`): Title of the venue. + address (:obj:`str`): Address of the venue. + foursquare_id (:obj:`str`): Optional. Foursquare identifier of the venue if known. + foursquare_type (:obj:`str`): Optional. Foursquare type of the venue, if known. + (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or + "food/icecream".) + google_place_id (:obj:`str`): Optional. Google Places identifier of the venue. + google_place_type (:obj:`str`): Optional. Google Places type of the venue. (See + `supported types `_.) + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the venue. + thumbnail_url (:obj:`str`): Optional. Url of the thumbnail for the result. + + .. versionadded:: 20.2 + thumbnail_width (:obj:`int`): Optional. Thumbnail width. + + .. versionadded:: 20.2 + thumbnail_height (:obj:`int`): Optional. Thumbnail height. + + .. versionadded:: 20.2 + + """ + + __slots__ = ( + "address", + "foursquare_id", + "foursquare_type", + "google_place_id", + "google_place_type", + "input_message_content", + "latitude", + "longitude", + "reply_markup", + "thumbnail_height", + "thumbnail_url", + "thumbnail_width", + "title", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + latitude: float, + longitude: float, + title: str, + address: str, + foursquare_id: Optional[str] = None, + foursquare_type: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + google_place_id: Optional[str] = None, + google_place_type: Optional[str] = None, + thumbnail_url: Optional[str] = None, + thumbnail_width: Optional[int] = None, + thumbnail_height: Optional[int] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.VENUE, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.latitude: float = latitude + self.longitude: float = longitude + self.title: str = title + self.address: str = address + + # Optional + self.foursquare_id: Optional[str] = foursquare_id + self.foursquare_type: Optional[str] = foursquare_type + self.google_place_id: Optional[str] = google_place_id + self.google_place_type: Optional[str] = google_place_type + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.thumbnail_url: Optional[str] = thumbnail_url + self.thumbnail_width: Optional[int] = thumbnail_width + self.thumbnail_height: Optional[int] = thumbnail_height diff --git a/inlinequeryresultvideo.py b/inlinequeryresultvideo.py new file mode 100644 index 0000000000000000000000000000000000000000..bb01c1ac1bd7b83ef5b5388e5a25cbdceb9960e7 --- /dev/null +++ b/inlinequeryresultvideo.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultVideo.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput +from telegram.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultVideo(InlineQueryResult): + """ + Represents a link to a page containing an embedded video player or a video file. By default, + this video file will be sent by the user with an optional caption. Alternatively, you can use + :attr:`input_message_content` to send a message with the specified content instead of + the video. + + Note: + If an InlineQueryResultVideo message contains an embedded video (e.g., YouTube), you must + replace its content using :attr:`input_message_content`. + + .. seealso:: :wiki:`Working with Files and Media ` + + .. versionchanged:: 20.5 + |removed_thumb_url_note| + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + video_url (:obj:`str`): A valid URL for the embedded video player or video file. + mime_type (:obj:`str`): Mime type of the content of video url, "text/html" or "video/mp4". + thumbnail_url (:obj:`str`, optional): URL of the thumbnail (JPEG only) for the video. + + .. versionadded:: 20.2 + + ..versionchanged:: 20.5 + |thumbnail_url_mandatory| + + title (:obj:`str`): Title for the result. + caption (:obj:`str`, optional): Caption of the video to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities + parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + + .. versionchanged:: 20.0 + |sequenceclassargs| + + video_width (:obj:`int`, optional): Video width. + video_height (:obj:`int`, optional): Video height. + video_duration (:obj:`int`, optional): Video duration in seconds. + description (:obj:`str`, optional): Short description of the result. + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the video. This field is required if + ``InlineQueryResultVideo`` is used to send an HTML-page as a result + (e.g., a YouTube video). + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: 21.3 + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VIDEO`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + video_url (:obj:`str`): A valid URL for the embedded video player or video file. + mime_type (:obj:`str`): Mime type of the content of video url, "text/html" or "video/mp4". + thumbnail_url (:obj:`str`): URL of the thumbnail (JPEG only) for the video. + + .. versionadded:: 20.2 + title (:obj:`str`): Title for the result. + caption (:obj:`str`): Optional. Caption of the video to be sent, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities + parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. + |captionentitiesattr| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + + video_width (:obj:`int`): Optional. Video width. + video_height (:obj:`int`): Optional. Video height. + video_duration (:obj:`int`): Optional. Video duration in seconds. + description (:obj:`str`): Optional. Short description of the result. + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the video. This field is required if + ``InlineQueryResultVideo`` is used to send an HTML-page as a result + (e.g., a YouTube video). + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: 21.3 + + """ + + __slots__ = ( + "caption", + "caption_entities", + "description", + "input_message_content", + "mime_type", + "parse_mode", + "reply_markup", + "show_caption_above_media", + "thumbnail_url", + "title", + "video_duration", + "video_height", + "video_url", + "video_width", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + video_url: str, + mime_type: str, + thumbnail_url: str, + title: str, + caption: Optional[str] = None, + video_width: Optional[int] = None, + video_height: Optional[int] = None, + video_duration: Optional[int] = None, + description: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence[MessageEntity]] = None, + show_caption_above_media: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.VIDEO, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.video_url: str = video_url + self.mime_type: str = mime_type + self.thumbnail_url: str = thumbnail_url + self.title: str = title + + # Optional + self.caption: Optional[str] = caption + self.parse_mode: ODVInput[str] = parse_mode + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.video_width: Optional[int] = video_width + self.video_height: Optional[int] = video_height + self.video_duration: Optional[int] = video_duration + self.description: Optional[str] = description + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/inlinequeryresultvoice.py b/inlinequeryresultvoice.py new file mode 100644 index 0000000000000000000000000000000000000000..d33f31b34d86e6e78764a27178fa6de22c2a7314 --- /dev/null +++ b/inlinequeryresultvoice.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InlineQueryResultVoice.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup +from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput +from telegram.constants import InlineQueryResultType + +if TYPE_CHECKING: + from telegram import InputMessageContent + + +class InlineQueryResultVoice(InlineQueryResult): + """ + Represents a link to a voice recording in an .ogg container encoded with OPUS. By default, + this voice recording will be sent by the user. Alternatively, you can use + :attr:`input_message_content` to send a message with the specified content instead of + the voice message. + + .. seealso:: :wiki:`Working with Files and Media ` + + Args: + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + voice_url (:obj:`str`): A valid URL for the voice recording. + title (:obj:`str`): Recording title. + caption (:obj:`str`, optional): Caption, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities + parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + + .. versionchanged:: 20.0 + |sequenceclassargs| + + voice_duration (:obj:`int`, optional): Recording duration in seconds. + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + message to be sent instead of the voice recording. + + Attributes: + type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VOICE`. + id (:obj:`str`): Unique identifier for this result, + :tg-const:`telegram.InlineQueryResult.MIN_ID_LENGTH`- + :tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. + voice_url (:obj:`str`): A valid URL for the voice recording. + title (:obj:`str`): Recording title. + caption (:obj:`str`): Optional. Caption, + 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities + parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + voice_duration (:obj:`int`): Optional. Recording duration in seconds. + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached + to the message. + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the + message to be sent instead of the voice recording. + + """ + + __slots__ = ( + "caption", + "caption_entities", + "input_message_content", + "parse_mode", + "reply_markup", + "title", + "voice_duration", + "voice_url", + ) + + def __init__( + self, + id: str, # pylint: disable=redefined-builtin + voice_url: str, + title: str, + voice_duration: Optional[int] = None, + caption: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional["InputMessageContent"] = None, + parse_mode: ODVInput[str] = DEFAULT_NONE, + caption_entities: Optional[Sequence[MessageEntity]] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + # Required + super().__init__(InlineQueryResultType.VOICE, id, api_kwargs=api_kwargs) + with self._unfrozen(): + self.voice_url: str = voice_url + self.title: str = title + + # Optional + self.voice_duration: Optional[int] = voice_duration + self.caption: Optional[str] = caption + self.parse_mode: ODVInput[str] = parse_mode + self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content diff --git a/inputcontactmessagecontent.py b/inputcontactmessagecontent.py new file mode 100644 index 0000000000000000000000000000000000000000..4060232bbed5e4d41fc97993ca8bb6938cb01527 --- /dev/null +++ b/inputcontactmessagecontent.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InputContactMessageContent.""" +from typing import Optional + +from telegram._inline.inputmessagecontent import InputMessageContent +from telegram._utils.types import JSONDict + + +class InputContactMessageContent(InputMessageContent): + """Represents the content of a contact message to be sent as the result of an inline query. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`phone_number` is equal. + + Args: + phone_number (:obj:`str`): Contact's phone number. + first_name (:obj:`str`): Contact's first name. + last_name (:obj:`str`, optional): Contact's last name. + vcard (:obj:`str`, optional): Additional data about the contact in the form of a vCard, + 0-:tg-const:`telegram.constants.ContactLimit.VCARD` bytes. + + Attributes: + phone_number (:obj:`str`): Contact's phone number. + first_name (:obj:`str`): Contact's first name. + last_name (:obj:`str`): Optional. Contact's last name. + vcard (:obj:`str`): Optional. Additional data about the contact in the form of a vCard, + 0-:tg-const:`telegram.constants.ContactLimit.VCARD` bytes. + + """ + + __slots__ = ("first_name", "last_name", "phone_number", "vcard") + + def __init__( + self, + phone_number: str, + first_name: str, + last_name: Optional[str] = None, + vcard: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + with self._unfrozen(): + # Required + self.phone_number: str = phone_number + self.first_name: str = first_name + # Optionals + self.last_name: Optional[str] = last_name + self.vcard: Optional[str] = vcard + + self._id_attrs = (self.phone_number,) diff --git a/inputinvoicemessagecontent.py b/inputinvoicemessagecontent.py new file mode 100644 index 0000000000000000000000000000000000000000..101e0184b5730b00e39d2d18e8542f6797e147fb --- /dev/null +++ b/inputinvoicemessagecontent.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains a class that represents a Telegram InputInvoiceMessageContent.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inputmessagecontent import InputMessageContent +from telegram._payment.labeledprice import LabeledPrice +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class InputInvoiceMessageContent(InputMessageContent): + """ + Represents the content of a invoice message to be sent as the result of an inline query. + + 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. + + .. versionadded:: 13.5 + + Args: + title (:obj:`str`): Product name. :tg-const:`telegram.Invoice.MIN_TITLE_LENGTH`- + :tg-const:`telegram.Invoice.MAX_TITLE_LENGTH` characters. + description (:obj:`str`): Product description. + :tg-const:`telegram.Invoice.MIN_DESCRIPTION_LENGTH`- + :tg-const:`telegram.Invoice.MAX_DESCRIPTION_LENGTH` characters. + payload (:obj:`str`): Bot-defined invoice payload. + :tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`- + :tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be displayed + to the user, use it for your internal processes. + provider_token (:obj:`str`): Payment provider token, obtained via + `@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. + currency (:obj:`str`): Three-letter ISO 4217 currency code, see more on + `currencies `_. + Pass ``XTR`` for payments in |tg_stars|. + prices (Sequence[:class:`telegram.LabeledPrice`]): Price breakdown, a list of + components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, + etc.). Must contain exactly one item for payments in |tg_stars|. + + .. versionchanged:: 20.0 + |sequenceclassargs| + + max_tip_amount (:obj:`int`, optional): The maximum accepted amount for tips in the + *smallest units* of the currency (integer, **not** float/double). For example, for a + maximum tip of ``US$ 1.45`` pass ``max_tip_amount = 145``. See the ``exp`` parameter in + `currencies.json `_, it + shows the number of digits past the decimal point for each currency (2 for the majority + of currencies). Defaults to ``0``. Not supported for payments in |tg_stars|. + suggested_tip_amounts (Sequence[:obj:`int`], optional): An array of suggested + amounts of tip in the *smallest units* of the currency (integer, **not** float/double). + At most 4 suggested tip amounts can be specified. The suggested tip amounts must be + positive, passed in a strictly increased order and must not exceed + :attr:`max_tip_amount`. + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + + provider_data (:obj:`str`, optional): An object for data about the invoice, + which will be shared with the payment provider. A detailed description of the required + fields should be provided by the payment provider. + photo_url (:obj:`str`, optional): URL of the product photo for the invoice. Can be a photo + of the goods or a marketing image for a service. People like it better when they see + what they are paying for. + photo_size (:obj:`int`, optional): Photo size. + photo_width (:obj:`int`, optional): Photo width. + photo_height (:obj:`int`, optional): Photo height. + need_name (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's full + name to complete the order. Ignored for payments in |tg_stars|. + need_phone_number (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's + phone number to complete the order. Ignored for payments in |tg_stars|. + need_email (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's email + address to complete the order. Ignored for payments in |tg_stars|. + need_shipping_address (:obj:`bool`, optional): Pass :obj:`True`, if you require the + user's shipping address to complete the order. Ignored for payments in |tg_stars| + send_phone_number_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's + phone number should be sent to provider. Ignored for payments in |tg_stars|. + send_email_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's email + address should be sent to provider. Ignored for payments in |tg_stars|. + is_flexible (:obj:`bool`, optional): Pass :obj:`True`, if the final price depends on + the shipping method. Ignored for payments in |tg_stars|. + + Attributes: + title (:obj:`str`): Product name. :tg-const:`telegram.Invoice.MIN_TITLE_LENGTH`- + :tg-const:`telegram.Invoice.MAX_TITLE_LENGTH` characters. + description (:obj:`str`): Product description. + :tg-const:`telegram.Invoice.MIN_DESCRIPTION_LENGTH`- + :tg-const:`telegram.Invoice.MAX_DESCRIPTION_LENGTH` characters. + payload (:obj:`str`): Bot-defined invoice payload. + :tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`- + :tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be displayed + to the user, use it for your internal processes. + provider_token (:obj:`str`): Payment provider token, obtained via + `@Botfather `_. Pass an empty string for payments in `Telegram + Stars `_. + currency (:obj:`str`): Three-letter ISO 4217 currency code, see more on + `currencies `_. + Pass ``XTR`` for payments in |tg_stars|. + prices (Tuple[:class:`telegram.LabeledPrice`]): Price breakdown, a list of + components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, + etc.). Must contain exactly one item for payments in |tg_stars|. + + .. versionchanged:: 20.0 + |tupleclassattrs| + + max_tip_amount (:obj:`int`): Optional. The maximum accepted amount for tips in the + *smallest units* of the currency (integer, **not** float/double). For example, for a + maximum tip of ``US$ 1.45`` ``max_tip_amount`` is ``145``. See the ``exp`` parameter in + `currencies.json `_, it + shows the number of digits past the decimal point for each currency (2 for the majority + of currencies). Defaults to ``0``. Not supported for payments in |tg_stars|. + suggested_tip_amounts (Tuple[:obj:`int`]): Optional. An array of suggested + amounts of tip in the *smallest units* of the currency (integer, **not** float/double). + At most 4 suggested tip amounts can be specified. The suggested tip amounts must be + positive, passed in a strictly increased order and must not exceed + :attr:`max_tip_amount`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + + provider_data (:obj:`str`): Optional. An object for data about the invoice, + which will be shared with the payment provider. A detailed description of the required + fields should be provided by the payment provider. + photo_url (:obj:`str`): Optional. URL of the product photo for the invoice. Can be a photo + of the goods or a marketing image for a service. People like it better when they see + what they are paying for. + photo_size (:obj:`int`): Optional. Photo size. + photo_width (:obj:`int`): Optional. Photo width. + photo_height (:obj:`int`): Optional. Photo height. + need_name (:obj:`bool`): Optional. Pass :obj:`True`, if you require the user's full name to + complete the order. Ignored for payments in |tg_stars|. + need_phone_number (:obj:`bool`): Optional. Pass :obj:`True`, if you require the user's + phone number to complete the order. Ignored for payments in |tg_stars|. + need_email (:obj:`bool`): Optional. Pass :obj:`True`, if you require the user's email + address to complete the order. Ignored for payments in |tg_stars|. + need_shipping_address (:obj:`bool`): Optional. Pass :obj:`True`, if you require the user's + shipping address to complete the order. Ignored for payments in |tg_stars|. + send_phone_number_to_provider (:obj:`bool`): Optional. Pass :obj:`True`, if user's phone + number should be sent to provider. Ignored for payments in |tg_stars|. + send_email_to_provider (:obj:`bool`): Optional. Pass :obj:`True`, if user's email address + should be sent to provider. Ignored for payments in |tg_stars|. + is_flexible (:obj:`bool`): Optional. Pass :obj:`True`, if the final price depends on the + shipping method. Ignored for payments in |tg_stars|. + + """ + + __slots__ = ( + "currency", + "description", + "is_flexible", + "max_tip_amount", + "need_email", + "need_name", + "need_phone_number", + "need_shipping_address", + "payload", + "photo_height", + "photo_size", + "photo_url", + "photo_width", + "prices", + "provider_data", + "provider_token", + "send_email_to_provider", + "send_phone_number_to_provider", + "suggested_tip_amounts", + "title", + ) + + def __init__( + self, + 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], + max_tip_amount: Optional[int] = None, + suggested_tip_amounts: Optional[Sequence[int]] = None, + provider_data: Optional[str] = None, + photo_url: Optional[str] = None, + photo_size: Optional[int] = None, + photo_width: Optional[int] = None, + photo_height: Optional[int] = None, + need_name: Optional[bool] = None, + need_phone_number: Optional[bool] = None, + need_email: Optional[bool] = None, + need_shipping_address: Optional[bool] = None, + send_phone_number_to_provider: Optional[bool] = None, + send_email_to_provider: Optional[bool] = None, + is_flexible: Optional[bool] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + with self._unfrozen(): + # Required + 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.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 + self.photo_url: Optional[str] = photo_url + self.photo_size: Optional[int] = photo_size + self.photo_width: Optional[int] = photo_width + self.photo_height: Optional[int] = photo_height + self.need_name: Optional[bool] = need_name + self.need_phone_number: Optional[bool] = need_phone_number + self.need_email: Optional[bool] = need_email + self.need_shipping_address: Optional[bool] = need_shipping_address + self.send_phone_number_to_provider: Optional[bool] = send_phone_number_to_provider + self.send_email_to_provider: Optional[bool] = send_email_to_provider + self.is_flexible: Optional[bool] = is_flexible + + self._id_attrs = ( + 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"]: + """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) + + return super().de_json(data=data, bot=bot) diff --git a/inputlocationmessagecontent.py b/inputlocationmessagecontent.py new file mode 100644 index 0000000000000000000000000000000000000000..d9642c485c5f21716e8749a797aeaa5131390e20 --- /dev/null +++ b/inputlocationmessagecontent.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InputLocationMessageContent.""" + +from typing import Final, Optional + +from telegram import constants +from telegram._inline.inputmessagecontent import InputMessageContent +from telegram._utils.types import JSONDict + + +class InputLocationMessageContent(InputMessageContent): + # fmt: off + """ + Represents the content of a location message to be sent as the result of an inline query. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`latitude` and :attr:`longitude` are equal. + + Args: + latitude (:obj:`float`): Latitude of the location in degrees. + longitude (:obj:`float`): Longitude of the location in degrees. + horizontal_accuracy (:obj:`float`, optional): The radius of uncertainty for the location, + measured in meters; 0- + :tg-const:`telegram.InputLocationMessageContent.HORIZONTAL_ACCURACY`. + live_period (:obj:`int`, optional): Period in seconds for which the location will be + updated, should be between + :tg-const:`telegram.InputLocationMessageContent.MIN_LIVE_PERIOD` and + :tg-const:`telegram.InputLocationMessageContent.MAX_LIVE_PERIOD` or + :tg-const:`telegram.constants.LocationLimit.LIVE_PERIOD_FOREVER` for live + locations that can be edited indefinitely. + heading (:obj:`int`, optional): For live locations, a direction in which the user is + moving, in degrees. Must be between + :tg-const:`telegram.InputLocationMessageContent.MIN_HEADING` and + :tg-const:`telegram.InputLocationMessageContent.MAX_HEADING` if specified. + proximity_alert_radius (:obj:`int`, optional): For live locations, a maximum distance + for proximity alerts about approaching another chat member, in meters. Must be + between :tg-const:`telegram.InputLocationMessageContent.MIN_PROXIMITY_ALERT_RADIUS` + and :tg-const:`telegram.InputLocationMessageContent.MAX_PROXIMITY_ALERT_RADIUS` + if specified. + + Attributes: + latitude (:obj:`float`): Latitude of the location in degrees. + longitude (:obj:`float`): Longitude of the location in degrees. + horizontal_accuracy (:obj:`float`): Optional. The radius of uncertainty for the location, + measured in meters; 0- + :tg-const:`telegram.InputLocationMessageContent.HORIZONTAL_ACCURACY`. + live_period (:obj:`int`): Optional. Period in seconds for which the location can be + updated, should be between + :tg-const:`telegram.InputLocationMessageContent.MIN_LIVE_PERIOD` and + :tg-const:`telegram.InputLocationMessageContent.MAX_LIVE_PERIOD`. + heading (:obj:`int`): Optional. For live locations, a direction in which the user is + moving, in degrees. Must be between + :tg-const:`telegram.InputLocationMessageContent.MIN_HEADING` and + :tg-const:`telegram.InputLocationMessageContent.MAX_HEADING` if specified. + proximity_alert_radius (:obj:`int`): Optional. For live locations, a maximum distance + for proximity alerts about approaching another chat member, in meters. Must be + between :tg-const:`telegram.InputLocationMessageContent.MIN_PROXIMITY_ALERT_RADIUS` + and :tg-const:`telegram.InputLocationMessageContent.MAX_PROXIMITY_ALERT_RADIUS` + if specified. + + """ + + __slots__ = ( + "heading", + "horizontal_accuracy", + "latitude", + "live_period", + "longitude", + "proximity_alert_radius") + # fmt: on + + def __init__( + self, + latitude: float, + longitude: float, + live_period: Optional[int] = None, + horizontal_accuracy: Optional[float] = None, + heading: Optional[int] = None, + proximity_alert_radius: Optional[int] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + with self._unfrozen(): + # Required + self.latitude: float = latitude + self.longitude: float = longitude + + # Optionals + self.live_period: Optional[int] = live_period + self.horizontal_accuracy: Optional[float] = horizontal_accuracy + self.heading: Optional[int] = heading + self.proximity_alert_radius: Optional[int] = ( + int(proximity_alert_radius) if proximity_alert_radius else None + ) + + self._id_attrs = (self.latitude, self.longitude) + + HORIZONTAL_ACCURACY: Final[int] = constants.LocationLimit.HORIZONTAL_ACCURACY + """:const:`telegram.constants.LocationLimit.HORIZONTAL_ACCURACY` + + .. versionadded:: 20.0 + """ + MIN_HEADING: Final[int] = constants.LocationLimit.MIN_HEADING + """:const:`telegram.constants.LocationLimit.MIN_HEADING` + + .. versionadded:: 20.0 + """ + MAX_HEADING: Final[int] = constants.LocationLimit.MAX_HEADING + """:const:`telegram.constants.LocationLimit.MAX_HEADING` + + .. versionadded:: 20.0 + """ + MIN_LIVE_PERIOD: Final[int] = constants.LocationLimit.MIN_LIVE_PERIOD + """:const:`telegram.constants.LocationLimit.MIN_LIVE_PERIOD` + + .. versionadded:: 20.0 + """ + MAX_LIVE_PERIOD: Final[int] = constants.LocationLimit.MAX_LIVE_PERIOD + """:const:`telegram.constants.LocationLimit.MAX_LIVE_PERIOD` + + .. versionadded:: 20.0 + """ + MIN_PROXIMITY_ALERT_RADIUS: Final[int] = constants.LocationLimit.MIN_PROXIMITY_ALERT_RADIUS + """:const:`telegram.constants.LocationLimit.MIN_PROXIMITY_ALERT_RADIUS` + + .. versionadded:: 20.0 + """ + MAX_PROXIMITY_ALERT_RADIUS: Final[int] = constants.LocationLimit.MAX_PROXIMITY_ALERT_RADIUS + """:const:`telegram.constants.LocationLimit.MAX_PROXIMITY_ALERT_RADIUS` + + .. versionadded:: 20.0 + """ diff --git a/inputmessagecontent.py b/inputmessagecontent.py new file mode 100644 index 0000000000000000000000000000000000000000..40088f5a4392f0319fa861ff03468c844dab8b7f --- /dev/null +++ b/inputmessagecontent.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InputMessageContent.""" + +from typing import Optional + +from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict + + +class InputMessageContent(TelegramObject): + """Base class for Telegram InputMessageContent Objects. + + See: :class:`telegram.InputContactMessageContent`, + :class:`telegram.InputInvoiceMessageContent`, + :class:`telegram.InputLocationMessageContent`, :class:`telegram.InputTextMessageContent` and + :class:`telegram.InputVenueMessageContent` for more details. + + """ + + __slots__ = () + + def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None: + super().__init__(api_kwargs=api_kwargs) + + self._freeze() diff --git a/inputtextmessagecontent.py b/inputtextmessagecontent.py new file mode 100644 index 0000000000000000000000000000000000000000..475f9c5bb28ee3748f112edc06c0a784bf5d501d --- /dev/null +++ b/inputtextmessagecontent.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InputTextMessageContent.""" +from typing import TYPE_CHECKING, Optional, Sequence, Tuple + +from telegram._inline.inputmessagecontent import InputMessageContent +from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_lpo_and_dwpp, parse_sequence_arg +from telegram._utils.defaultvalue import DEFAULT_NONE +from telegram._utils.types import JSONDict, ODVInput + +if TYPE_CHECKING: + from telegram._linkpreviewoptions import LinkPreviewOptions + + +class InputTextMessageContent(InputMessageContent): + """ + Represents the content of a text message to be sent as the result of an inline query. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`message_text` is equal. + + Examples: + :any:`Inline Bot ` + + Args: + message_text (:obj:`str`): Text of the message to be sent, + :tg-const:`telegram.constants.MessageLimit.MIN_TEXT_LENGTH`- + :tg-const:`telegram.constants.MessageLimit.MAX_TEXT_LENGTH` characters after entities + parsing. + parse_mode (:obj:`str`, optional): |parse_mode| + entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + + .. versionchanged:: 20.0 + |sequenceclassargs| + + link_preview_options (:obj:`LinkPreviewOptions`, optional): Link preview generation + options for the message. Mutually exclusive with + :paramref:`disable_web_page_preview`. + + .. versionadded:: 20.8 + + Keyword Args: + disable_web_page_preview (:obj:`bool`, optional): Disables link previews for links in the + sent message. Convenience parameter for setting :paramref:`link_preview_options`. + Mutually exclusive with :paramref:`link_preview_options`. + + .. versionchanged:: 20.8 + Bot API 7.0 introduced :paramref:`link_preview_options` replacing this + argument. PTB will automatically convert this argument to that one, but + for advanced options, please use :paramref:`link_preview_options` directly. + + .. versionchanged:: 21.0 + |keyword_only_arg| + + Attributes: + message_text (:obj:`str`): Text of the message to be sent, + :tg-const:`telegram.constants.MessageLimit.MIN_TEXT_LENGTH`- + :tg-const:`telegram.constants.MessageLimit.MAX_TEXT_LENGTH` characters after entities + parsing. + parse_mode (:obj:`str`): Optional. |parse_mode| + entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + + .. versionchanged:: 20.0 + + * |tupleclassattrs| + * |alwaystuple| + link_preview_options (:obj:`LinkPreviewOptions`): Optional. Link preview generation + options for the message. + + .. versionadded:: 20.8 + + """ + + __slots__ = ("entities", "link_preview_options", "message_text", "parse_mode") + + def __init__( + self, + message_text: str, + parse_mode: ODVInput[str] = DEFAULT_NONE, + entities: Optional[Sequence[MessageEntity]] = None, + link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, + *, + disable_web_page_preview: Optional[bool] = None, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + + with self._unfrozen(): + # Required + self.message_text: str = message_text + # Optionals + self.parse_mode: ODVInput[str] = parse_mode + self.entities: Tuple[MessageEntity, ...] = parse_sequence_arg(entities) + self.link_preview_options: ODVInput[LinkPreviewOptions] = parse_lpo_and_dwpp( + disable_web_page_preview, link_preview_options + ) + + self._id_attrs = (self.message_text,) diff --git a/inputvenuemessagecontent.py b/inputvenuemessagecontent.py new file mode 100644 index 0000000000000000000000000000000000000000..016969b2256a3bad6ac6d05e330874b45b58e694 --- /dev/null +++ b/inputvenuemessagecontent.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the classes that represent Telegram InputVenueMessageContent.""" +from typing import Optional + +from telegram._inline.inputmessagecontent import InputMessageContent +from telegram._utils.types import JSONDict + + +class InputVenueMessageContent(InputMessageContent): + """Represents the content of a venue message to be sent as the result of an inline query. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`latitude`, :attr:`longitude` and :attr:`title` + are equal. + + Note: + Foursquare details and Google Pace details are mutually exclusive. However, this + behaviour is undocumented and might be changed by Telegram. + + Args: + latitude (:obj:`float`): Latitude of the location in degrees. + longitude (:obj:`float`): Longitude of the location in degrees. + title (:obj:`str`): Name of the venue. + address (:obj:`str`): Address of the venue. + foursquare_id (:obj:`str`, optional): Foursquare identifier of the venue, if known. + foursquare_type (:obj:`str`, optional): Foursquare type of the venue, if known. + (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or + "food/icecream".) + google_place_id (:obj:`str`, optional): Google Places identifier of the venue. + google_place_type (:obj:`str`, optional): Google Places type of the venue. (See + `supported types `_.) + + Attributes: + latitude (:obj:`float`): Latitude of the location in degrees. + longitude (:obj:`float`): Longitude of the location in degrees. + title (:obj:`str`): Name of the venue. + address (:obj:`str`): Address of the venue. + foursquare_id (:obj:`str`): Optional. Foursquare identifier of the venue, if known. + foursquare_type (:obj:`str`): Optional. Foursquare type of the venue, if known. + (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or + "food/icecream".) + google_place_id (:obj:`str`): Optional. Google Places identifier of the venue. + google_place_type (:obj:`str`): Optional. Google Places type of the venue. (See + `supported types `_.) + + """ + + __slots__ = ( + "address", + "foursquare_id", + "foursquare_type", + "google_place_id", + "google_place_type", + "latitude", + "longitude", + "title", + ) + + def __init__( + self, + latitude: float, + longitude: float, + title: str, + address: str, + foursquare_id: Optional[str] = None, + foursquare_type: Optional[str] = None, + google_place_id: Optional[str] = None, + google_place_type: Optional[str] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + with self._unfrozen(): + # Required + self.latitude: float = latitude + self.longitude: float = longitude + self.title: str = title + self.address: str = address + # Optionals + self.foursquare_id: Optional[str] = foursquare_id + self.foursquare_type: Optional[str] = foursquare_type + self.google_place_id: Optional[str] = google_place_id + self.google_place_type: Optional[str] = google_place_type + + self._id_attrs = ( + self.latitude, + self.longitude, + self.title, + ) diff --git a/known_hosts b/known_hosts new file mode 100644 index 0000000000000000000000000000000000000000..4f21d4ff84dce790a0f3cc56e718b301f8da5b26 --- /dev/null +++ b/known_hosts @@ -0,0 +1 @@ +hf.co ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINJjhgtT9FOQrsVSarIoPVI1jFMh3VSHdKfdqp/O776s diff --git a/py.typed b/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..80edfde44f80a77a68331373aed43fc3e29bd63d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,244 @@ +# PACKAGING +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +dynamic = ["version"] +name = "python-telegram-bot" +description = "We have made you a wrapper you can't refuse" +readme = "README.rst" +requires-python = ">=3.8" +license = "LGPL-3.0-only" +license-files = { paths = ["LICENSE", "LICENSE.dual", "LICENSE.lesser"] } +authors = [ + { name = "Leandro Toledo", email = "devs@python-telegram-bot.org" } +] +keywords = [ + "python", + "telegram", + "bot", + "api", + "wrapper", +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", + "Operating System :: OS Independent", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Communications :: Chat", + "Topic :: Internet", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", +] +dependencies = [ + "httpx ~= 0.27", +] + +[project.urls] +"Homepage" = "https://python-telegram-bot.org" +"Documentation" = "https://docs.python-telegram-bot.org" +"Bug Tracker" = "https://github.com/python-telegram-bot/python-telegram-bot/issues" +"Source Code" = "https://github.com/python-telegram-bot/python-telegram-bot" +"News" = "https://t.me/pythontelegrambotchannel" +"Changelog" = "https://docs.python-telegram-bot.org/en/stable/changelog.html" +"Support" = "https://t.me/pythontelegrambotgroup" + +[project.optional-dependencies] +# Make sure to install those as additional_dependencies in the +# pre-commit hooks for pylint & mypy +# Also update the readme accordingly +# +# When dependencies release new versions and tests succeed, we should try to expand the allowed +# versions and only increase the lower bound if necessary +# +# When adding new groups, make sure to update `ext` and `all` accordingly + +# Optional dependencies for production +all = [ + "python-telegram-bot[ext,http2,passport,socks]", +] +callback-data = [ + # Cachetools doesn't have a strict stability policy. Let's be cautious for now. + "cachetools>=5.3.3,<5.6.0", +] +ext = [ + "python-telegram-bot[callback-data,job-queue,rate-limiter,webhooks]", +] +http2 = [ + "httpx[http2]", +] +job-queue = [ + # APS doesn't have a strict stability policy. Let's be cautious for now. + "APScheduler~=3.10.4", + # pytz is required by APS and just needs the lower bound due to #2120 + "pytz>=2018.6", +] +passport = [ + "cryptography!=3.4,!=3.4.1,!=3.4.2,!=3.4.3,>=39.0.1", + # cffi is a dependency of cryptography and added support for python 3.13 in 1.17.0rc1 + "cffi >= 1.17.0rc1; python_version > '3.12'" +] +rate-limiter = [ + "aiolimiter~=1.1.0", +] +socks = [ + "httpx[socks]", +] +webhooks = [ + # tornado is rather stable, but let's not allow the next major release without prior testing + "tornado~=6.4", +] + + +# HATCH +[tool.hatch.version] +# dynamically evaluates the `__version__` variable in that file +source = "code" +path = "telegram/_version.py" +search-paths = ["telegram"] + +[tool.hatch.build] +packages = ["telegram"] + +# BLACK: +[tool.black] +line-length = 99 + +# ISORT: +[tool.isort] # black config +profile = "black" +line_length = 99 + +# RUFF: +[tool.ruff] +line-length = 99 +show-fixes = true + +[tool.ruff.lint] +preview = true +explicit-preview-rules = true # TODO: Drop this when RUF022 and RUF023 are out of preview +ignore = ["PLR2004", "PLR0911", "PLR0912", "PLR0913", "PLR0915", "PERF203"] +select = ["E", "F", "I", "PL", "UP", "RUF", "PTH", "C4", "B", "PIE", "SIM", "RET", "RSE", + "G", "ISC", "PT", "ASYNC", "TCH", "SLOT", "PERF", "PYI", "FLY", "AIR", "RUF022", + "RUF023", "Q", "INP", "W", "YTT", "DTZ", "ARG", "T20", "FURB", "DOC", "TRY", + "D100", "D101", "D102", "D103", "D300", "D418", "D419", "S"] +# Add "A (flake8-builtins)" after we drop pylint + +[tool.ruff.lint.per-file-ignores] +"tests/*.py" = ["B018"] +"tests/**.py" = ["RUF012", "ASYNC230", "DTZ", "ARG", "T201", "ASYNC109", "D", "S", "TRY"] +"telegram/**.py" = ["TRY003"] +"telegram/ext/_applicationbuilder.py" = ["TRY004"] +"telegram/ext/filters.py" = ["D102"] +"docs/**.py" = ["INP001", "ARG", "D", "TRY003", "S"] +"examples/**.py" = ["ARG", "D", "S105", "TRY003"] + +[tool.ruff.lint.pydocstyle] +convention = "google" + +# PYLINT: +[tool.pylint."messages control"] +enable = ["useless-suppression"] +disable = ["duplicate-code", "too-many-arguments", "too-many-public-methods", + "too-few-public-methods", "broad-exception-caught", "too-many-instance-attributes", + "fixme", "missing-function-docstring", "missing-class-docstring", "too-many-locals", + "too-many-lines", "too-many-branches", "too-many-statements", "cyclic-import" +] + +[tool.pylint.main] +# run pylint across multiple cpu cores to speed it up- +# https://pylint.pycqa.org/en/latest/user_guide/run.html?#parallel-execution to know more +jobs = 0 + +[tool.pylint.classes] +exclude-protected = ["_unfrozen"] + +# PYTEST: +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "--no-success-flaky-report -rX" +filterwarnings = [ + "error", + "ignore::DeprecationWarning", + 'ignore:Tasks created via `Application\.create_task` while the application is not running', + "ignore::ResourceWarning", + # TODO: Write so good code that we don't need to ignore ResourceWarnings anymore + # Unfortunately due to https://github.com/pytest-dev/pytest/issues/8343 we can't have this here + # and instead do a trick directly in tests/conftest.py + # ignore::telegram.utils.deprecate.TelegramDeprecationWarning +] +markers = [ + "dev", # If you want to test a specific test, use this + "no_req", + "req", +] +asyncio_mode = "auto" +log_format = "%(funcName)s - Line %(lineno)d - %(message)s" +# log_level = "DEBUG" # uncomment to see DEBUG logs + +# MYPY: +[tool.mypy] +warn_unused_ignores = true +warn_unused_configs = true +disallow_untyped_defs = true +disallow_incomplete_defs = true +disallow_untyped_decorators = true +show_error_codes = true +python_version = "3.8" + +# For some files, it's easier to just disable strict-optional all together instead of +# cluttering the code with `# type: ignore`s or stuff like +# `if self.text is None: raise RuntimeError()` +[[tool.mypy.overrides]] +module = [ + "telegram._callbackquery", + "telegram._file", + "telegram._message", + "telegram._files.file" +] +strict_optional = false + +# type hinting for asyncio in webhookhandler is a bit tricky because it depends on the OS +[[tool.mypy.overrides]] +module = "telegram.ext._utils.webhookhandler" +warn_unused_ignores = false + +# The libs listed below are only used for the `customwebhookbot_*.py` examples +# let's just ignore type checking for them for now +[[tool.mypy.overrides]] +module = [ + "flask.*", + "quart.*", + "starlette.*", + "uvicorn.*", + "asgiref.*", + "django.*", + "apscheduler.*", # not part of `customwebhookbot_*.py` examples +] +ignore_missing_imports = true + +# COVERAGE: +[tool.coverage.run] +branch = true +source = ["telegram"] +parallel = true +concurrency = ["thread", "multiprocessing"] +omit = [ + "tests/", + "telegram/__main__.py" +] + +[tool.coverage.report] +exclude_also = [ + "@overload", + "@abstractmethod", + "if TYPE_CHECKING:" +] diff --git a/requirements-dev-all.txt b/requirements-dev-all.txt new file mode 100644 index 0000000000000000000000000000000000000000..995e067c4206ce5e8fc1fecd9cfd3e9cf8810a0a --- /dev/null +++ b/requirements-dev-all.txt @@ -0,0 +1,5 @@ +-e .[all] +# needed for pre-commit hooks in the git commit command +pre-commit +-r requirements-unit-tests.txt +-r docs/requirements-docs.txt diff --git a/requirements-unit-tests.txt b/requirements-unit-tests.txt new file mode 100644 index 0000000000000000000000000000000000000000..df1e83b45732101d569e699ced1e834f397d9777 --- /dev/null +++ b/requirements-unit-tests.txt @@ -0,0 +1,19 @@ +-e . + +# required for building the wheels for releases +build + +# For the test suite +pytest==8.3.2 + +# needed because pytest doesn't come with native support for coroutines as tests +pytest-asyncio==0.21.2 + +# xdist runs tests in parallel +pytest-xdist==3.6.1 + +# Used for flaky tests (flaky decorator) +flaky>=3.8.1 + +# used in test_official for parsing tg docs +beautifulsoup4 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000000000000000000000000000000000000..c24e78bc4e1eae7666d466572cf1bea1e6a6642a --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[flake8] +max-line-length = 99 +ignore = W503, W605 +extend-ignore = E203, E704 +exclude = docs/source/conf.py diff --git a/v12.5-v20.0b0.gpg b/v12.5-v20.0b0.gpg new file mode 100644 index 0000000000000000000000000000000000000000..d3e38fbeeb270ea04fbe83a917acfa68f457d8ab --- /dev/null +++ b/v12.5-v20.0b0.gpg @@ -0,0 +1,65 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBF6AWOIBEAClMRtGdbm2yKAo1GdeybmasMpsCBmGI952/z5VvjSgDlu+ktPJ +QauWkARWrq4Eab2uZEIQrHUw01+9/Ugfu7qHu4safZjc177Tq4Fp0hJDHFRFVdqb +68Rv5mH0xjr7At0eNys2Rx8m7H1dBYQCz4aroUQ0q0TB4qSeKU9FzUzOZZ7/pYri +KFyNdaIjhrBeY/WXbn+7L2/cFJtafJHqkZlyfIgQzpmONfIrtJLnG0nxdBPIxg3m +CYxzrJKCkPPrl8b4TNV0LCO9c4r8zXGiZ6ZOQMAGjgb2Do67AvHTLI3YYd8+iY25 +112noIFASabxbW9jBb+nlyMc8nz5kW19JWq6sBfMJYzWaVpI6ofNpiZtKWupIt2V +4GcViZmE/Kh4hBQjrBIAeqLUz6ABidERrhWu6/wP3huNe+R6eMQTfEdYlYeD3LXG +5Upl46/wnodWQeW4llMVBhjkS+5ExtvlVBD9Yz8iQMUsXUSu8+UPRgjy6YIRsvtF +bzGKFXkn4tFWCyQsXJDf8f+lQCtB1XF/DN2IVZF9OjQOvrpRwJk3h3CiQboN5lf7 +Mj7I11MusPKzn34vg+3f6loOOQ++GUa+WH2B6Dx6AH3C0BI0V2sh6axu6HYJNo3o +stOXzhhWysPKp9pj41fmaFTcF8bYYlnHoLA17IapttsYo0WuFaBF5UW90QARAQAB +tCpIaW5yaWNoIE1haGxlciA8aGlucmljaC5tYWhsZXJAZnJlZW5ldC5kZT6JAk4E +EwEKADgWIQRlW7T1bNsODkUAz1cung4SfvPygwUCXoBY4gIbAwULCQgHAgYVCgkI +CwIEFgIDAQIeAQIXgAAKCRAung4SfvPyg3fXD/9IMuDgYGl+T98JvN/VHLcrA9rT +idIWr5vDSxjniQaYe8Kc3oTqje1xmRcIR7z3WFKgxErx8yEf3AOUCPqQh53MjS0q +rXNDCoRWqlljpeY2EHZh2xPSvin2pUgOd334BRFeBqyvIfb/IQmwUNDoUp1FxKEq +4veT4XPDnKZlZs/OwRMD1f0v64mtTy60fQzJyM9ICLWzZdCKZULGTKCBLms63l0h +wqznU6RA7qEpKeLEwwZs454maPdDraOebyhBYqf2EbATo1Rf1faqNzjL2T7KtyZg +PB1RDah/tCrsRi2ueFbOakyDms+98Km94lC8cZ0Wp7wlXaBLQHbRgylt49dN5KLP +cgjUxnBDOxM/4iCrwLQ3GxqZksOkwuzkZE3v1BkRy51XPRiaEov+57zhua686BzY +/rQjG6xWEawmodydUPd7qZ8LbO0bGpw+euqVd+FDEmHi4ytB1vyZMQ3Zt7gZjbBm +Wxd0x4TSKz0CQ4XuGxXvVx0ZXv3zbOXE2g06UmxijHbCwNZQazAzOw5EVaZiZPvK +4HzxssBskp49xh+8T8Pgmc085ujstW2+b1XiZcqnVhIkOqEynxKhPghP/5ODtY7/ +13dkqbVgAfTZ8JU6Q+jTo5bhaoFohnb8XeEg7O8aLf06WpKvorJ+O8XGKICn1tnD +odL5XTa9xX8WMwiI3bQ9SGlucmljaCBNYWhsZXIgPDIyMzY2NTU3K0JpYm8tSm9z +aGlAdXNlcnMubm9yZXBseS5naXRodWIuY29tPokCTgQTAQoAOBYhBGVbtPVs2w4O +RQDPVy6eDhJ+8/KDBQJjm4maAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJ +EC6eDhJ+8/KDKTcP/ja9/nVePimncHGvrT70Ek53qBheSTujurDtNE6mfUOxOgr8 +7m/fcZo3j/3FUssN81ijoNthS6HuhE0kyc+jbL1q4cv9oOeqgniV0P2GxlCOLwrJ +k3BjLNA04P8qSXfS1eefyRgr+dtNaOw2ARetOZm3yja34EW8II6p7Bd7DTRoGOAQ +cwi0Xz0AGyz9drLS0vDZ3VJfB0xIUToS/I/PBjUsIg0eD9M2bEOznFHK422CQMJV +r4pdHJgMfzX7XmwqMQP31oz8zNljsLWYZqpMR9Lw3B2XKziaZlptXgwC9uulcojG +lZv1OTXFKudjKmuOgzd9tW0nwMX/XnNGC7rzCpJCi5//8LeqbLxFqHoK8fXGs/9k +U7EBd3srEnGZHp3ROCsEGmNERTTIIN/rWS3lywnQCQ5kyk8HjT3WztfyLuAIiU+q +fiGQvvkehFF31ONa6tA5Xsm/0ch8rFx9ON8K72KY0fHLJ4FMz0rIdUa47GDYKDAr +n8q+drVCGBVSOWqHYhSZ5C/NCtRr6TebGCsxmhsVuqcXpJSM8CqG8f2xYeQeRw54 +bGSrCGMB5TlcMUKQ9wmQWHDHa+khobvYrGBtZaHMt/exnBYqwPna88rp1pWyhIqw +bBiCzKPYOUvzW+nUL0ogCVTfq5XKMJAQh1d1FPPbSLIb/0LYSHC4FavvrmJ3uQIN +BF6AWOIBEADUaTuRBlOcwBBZAFYHgXNM22+TdU+JcYVbdn96A0NPJy0z91SLHotM +w8fr9Sbd/g+xhG8byeFDpdEwTz+sH3GN3SjaSRV3CYIueWApiAFC4ybIoR46kcri +DQJWaTL8YtofBQs7GTtmZ/IbKaxRC/nKXalBnUh3nPgWCLUcIu8axmloBfHP9zFz ++Vn37JrClM66V2TLeJp5/C3Sw2lR2Dj13G65LkSAOX+naoL//PWSSmnfiDef2DA6 +Ucd3vh2BoTVkmZoDeLkIP93MfDM23I7NRq9rQSptcvYmtdxo3f3IU25sfjYBi+3g +HIWzbFq2SBeGAbWBufcFkj+kvoP4LzxJ63hVhpA0ql3WWgi5eKOeuhtnBSNaejhC +Axq8ktdAFUpyLMjA53bMUnD7Xkb09tMwW0OcUFPxVz3LG7TT8aQSwA46u/nBFIDN +4D5R14OdAysLHcVp536NSApfvzc/E5OCIx+0TPihq+8EnSEyHMvNXA0QLeGcSn8a +CoIjRK9p7IpKknGNCBx/H54rHGWi0XdPZPNbGHGFypualEB2uIRELFFkCquO2DTi +rYnrYM2Xf/u1ahp1vLNGgM1F6L9u+mC6gUUMGmyBklsz1jTGkTB4HmiMDLBDhAQM +/5UPJQhAdxuZwdfyveGxWYBJSSMtGbR+yOfT7NuTEhtBj0WG4h6tPwARAQABiQI2 +BBgBCgAgFiEEZVu09WzbDg5FAM9XLp4OEn7z8oMFAl6AWOICGwwACgkQLp4OEn7z +8oOZ1g/+NE9vZSoxB9yw8uLRthjUUScES2wpA4VBVgpcA0QNEqtq5xE82xXUMhIb +1td6E00+lESoXGo66xoIFQdWEMyr0Df6kfFSTgh2FL4DA5NSSQgiM9u4O8yayu2x +AmBQzRz42V3AquVSH7gu/WhwPcYROq+gYaQT7D/l6Mg/mRUfMS/zvkFIsTJktp+q +yvcuf8ybqoCetDbOw6Uo12IjfxqT1pj8gUVrN/ePlI3HXDzOS6bCiYCgRjCU/Ujj +zcXOupKjA4mvN6LCslgwREciZgDE5W31Ghnjd3tT4OL9TSixugRFD67jr7ZsXpcl +ZReF+nbEsTZlKMXKVDnvwgvrVD4BolSj9rBpPcyftcZtFeuBPSVUE1c6pd5Vo7HJ +I+QDHQA32/hk8dDdqrdVHCn24+yZUkpkuzvNovVJINqt/4GgIRUZot0e5MupFuj9 +D+Nn90ffKvFe1YjmRkJ1hblHG9Lh2I4LQOjF2qGKRL+CmtgA3NbO4BKLuTEAJnJz +PeR4FOX756R40p9Z7e0XhUqF4D/GvRyfxYhxkMKbIGEXbfgPjcNvVCDJxElBBtMz +mu6lniNOz/rViivD28dWHIro3ScaGsB4vAo2Eq+4VWTUtvoPo0OHbfX8PDbpcTuU +3a3DN3/RD8DQe+Pm5clbVCpDHzKThq9TpHFC0Iozh6d7QY+hQac= +=VeuH +-----END PGP PUBLIC KEY BLOCK----- diff --git a/v20.0-v21.3.gpg b/v20.0-v21.3.gpg new file mode 100644 index 0000000000000000000000000000000000000000..7e61705189e048287b145b4e75d0c8f176ffa508 --- /dev/null +++ b/v20.0-v21.3.gpg @@ -0,0 +1,53 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGOgNtUBEAC9CnrQYcKeHOVQatup9batI7lOSoXhqnS4v41LmWKt2GgVpiSn +CpWre04UMg+s/oCFto6af97vGxSsGbb2b23mTVHECvUoUBsiM2CMHopvNpMiPG3s +85gIaqQPOyVorET+MgPw96N+Ik/8sevOTb7u6+tUP4wdZcplVXBo5q+3EeK5GDb3 +g+WNYbcwZ+x+3jwgphQmryHAXAis4PrdAfuaAXxclN/prJ23245IaAQxWID1Mcxi +ZaT/vZSqDEKbjj3wyzlJBfe5DuAG2IqAqEbWZCYUjjdUrsrGVdjh6Al1CqA+V8px +PYCF+qK6c3M7ilbQSXv9dDjiH1shM7DPrXop657zFandVrmaFVcf7/WLLNSX0KwM +kuYdFXYQXXoptpLsB6edB8Od39l6uETObDDVXiKQdeBypFlkWaqDv2soBY04+o0h +NvQBWT3EPS2zUXGwo7j2pd1Q4j1qC0ceKW64rxF8UqtZXOG2mJrVKeKvbprhq3kF +3+vXHqTgr6ZgjplHkVZmcv31UOBlwGnN+xBi5ooD/brF0rxZ9Pcr0BXdtBhG80V+ +q1vxVh9D2K0RFJ7pLaxFdExZoevOwHdtwT411I9VfWdICc3w9KYnH4xSwjoR+Cm2 +Jy9aTlft+TkQXfOxWD83QUtu/zE/MYwp2xhHLsJoOTzSLHcRHMOWC4yfrQARAQAB +tG9IaW5yaWNoIE1haGxlciAoS2V5IGZvciBzaWduaW5nIHJlbGVhc2VzIG9mIHB5 +dGhvbi10ZWxlZ3JhbS1ib3QpIDwyMjM2NjU1NytCaWJvLUpvc2hpQHVzZXJzLm5v +cmVwbHkuZ2l0aHViLmNvbT6JAk4EEwEKADgWIQRMulGIRwROKJVIvZ+iuYSpBzAi +sgUCY6A21QIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRCiuYSpBzAisgG+ +EACXEqRSzuMI/Ca4vs9Q0teaUskjDbmb3tpt/vlPtgtWVBxIwixIrBXXt6VF58pc +4PjnmWd0TQVWHXysVtV1JBdFHJPf7CXD1OXAzDYLS3s0gZJ8Y32oG+94FKVtdcjp +M9XV2ip0DZFd0k8ln0bVultFPR441Q7DVpJFctAvICvccC//TkgD0vMWAV+zKD/9 +xctkIa1t0ZQ2xHA+NPDTLEGzs9rADwJTK/OOE528mo8ki37RTM7Ou6kGF9Xb+A5P +iaO2FBDp3b6QyprMvaOF2Xgt18h+wembzsXwVwep3OpjZHYsaImoWn6821HmsS7H +MTjR71aG6BrGZTjFtsWNd9Anu2aYfyZgpZZmeTMC6o81x428YEA9Mr7+yAgFAfd7 +BTyShNsyP1LcbMpPoYciFPiL9ieMqfNPOpdluV56Xb4W27/EJ39wx+PQPEs/5A2n +RUfKdtJtqxrGGOWtoLORS0pr+s7JS6WP1dfR/VWeTMiVNG506crAvCkN7VvTlpI0 +ASqnRvFeu82cgOiP8toZJ/seRDq5JdRy/dV6EMD442mE6si7RUFJ3ShQsAMdukGw +lCDdUfLATJ/rrMCsoaInIbqWBa49OqEKcpOxyR2qR/91wxS1LDhfkmtumGgIxkjj +utt2XKLSaLNaXIPjX19RMqmFntzMrU2Mk+0HD40dd30bTbkCDQRjoDbVARAA1tN/ +R2PhQ++bbLCtqeIjmkx/k5nmLK5P52lOu1zR142XVDC1ByZ8FEMzgnU+n660HN2g +62/p7lrMi1I12RuPZJBvalsRfGVx2DtbX06AEEg8bjABXB48uF1xU35gLOt1nhDp +bEinY0Kx4uBQcXlKme83ceyG2vmZvW0C+2JyLFFjwpUvOwzlzjSMkBFdbDzMBL93 +A8gTt36WxcA+IbZlEwYte6w8FE5HcEZgGXsYXCvKEEN2wn3scXg+1R8bXOyQeRCo +KqblMDRO6Qsb/QL3a86so7MBWX4VmW+7714kaQYhMC+DKZq5u7yO//UqnEElHrYw +W8T5KGCYTizXcrM+4giAecdeukdPpeLwOmLhKQ1YfwWkHolsruwjg15vW4QE85dV +LRSTdgH1Is6NCqweOMIozH6uIAYz3b1u1r3C9hlgMqFN1oujI12MjOURLtLUtrN/ +LeosSZEUgL3T7a1bmO4ykcVvhWvGrcU0HfR/CX5nXMymhkjGk2x47elottK/O3BP +evinQWPSiF+ABH3605KZQB2cWcr0oH1rLMAIwcYtMdCmGJY9we1fRF2lf/BTcoui +nDqsuuIJJpHg9FIIR167fW1tQH0DYbQBvsDCG6jv5pZ7V4Rl8zJ30Am5h1XZzpIk +/3wPvouskdfsEF1CahpzKF/rdtIB5aaynWO0xHkAEQEAAYkCNgQYAQoAIBYhBEy6 +UYhHBE4olUi9n6K5hKkHMCKyBQJjoDbVAhsMAAoJEKK5hKkHMCKypCcP/3pvvpP/ +axelSKUgdsmU+mcUK4AHWtPXZxJcLoIGhMEOiNOdeX64XhHYGO0gKDvPn1Bv/ygI +ef49+NGAXWZmaSOqXSKfSWaWwQqlGLuqL76Hes5EqTbAlo4qEEbZoWx/x52zbXz6 +1fbcH+rU+3dYVFIzvq2UG2t3L6v+PQXoZL20CWxIAWLGjVmnUPV/7/XJJATg2x1J +IEb7iG9h8XLvC4/+whRWG5J48kPRRPE3vy1kgeUtvHCIsvujoFCosAxRhLR62+5x +F4rREUyY2WKAsuymVCJPEKE/g3LoTtn+f78ucECm6MJNhJpZqdjlqib0mGhjWGeR +XPNHL3iEL4wFlU/DRt25GE7YKTHoPFcjet4wWOUdj9OHdV2DHgsgETEAVxaFwH7W +hnPBtyc9TDLsTtJ1NphxofSiehsesNrfX5sneQVBpUclb5HS8vYqJxLC0KLOulNU +emDwYaQdM4chy1QfKHJhj9uSbqRcoUcms59mLINUlTmdwez2Vbmnh8sLYfuz2S0L +7OANYTUOOmwlw9cQJjF2hgtgvn7lRK/zD38nVesHSBU4dmlYISV5/jnByhIDaIdo +Afq07AF3OUKAI2FfAquDjV2P66GS1K2cWEQAAd3wgze3Qlt7xvifGTxHEXXesfP3 +toI3+U6jDIfsuoL4ynzcPdz0IsFwu+yX6bxw +=gtS+ +-----END PGP PUBLIC KEY BLOCK----- diff --git a/warnings.py b/warnings.py new file mode 100644 index 0000000000000000000000000000000000000000..0c761b97421cdb479999ca6dd638dc17bddc4805 --- /dev/null +++ b/warnings.py @@ -0,0 +1,87 @@ +#! /usr/bin/env python +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains classes used for warnings issued by this library. + +.. versionadded:: 20.0 +""" + +__all__ = ["PTBDeprecationWarning", "PTBRuntimeWarning", "PTBUserWarning"] + + +class PTBUserWarning(UserWarning): + """ + Custom user warning class used for warnings in this library. + + .. seealso:: :wiki:`Exceptions, Warnings and Logging ` + + .. versionadded:: 20.0 + """ + + __slots__ = () + + +class PTBRuntimeWarning(PTBUserWarning, RuntimeWarning): + """ + Custom runtime warning class used for warnings in this library. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + +# https://www.python.org/dev/peps/pep-0565/ recommends using a custom warning class derived from +# DeprecationWarning. We also subclass from PTBUserWarning so users can easily 'switch off' +# warnings +class PTBDeprecationWarning(PTBUserWarning, DeprecationWarning): + """ + Custom warning class for deprecations in this library. + + .. versionchanged:: 20.0 + Renamed TelegramDeprecationWarning to PTBDeprecationWarning. + + Args: + version (:obj:`str`): The version in which the feature was deprecated. + + .. versionadded:: 21.2 + message (:obj:`str`): The message to display. + + .. versionadded:: 21.2 + + Attributes: + version (:obj:`str`): The version in which the feature was deprecated. + + .. versionadded:: 21.2 + message (:obj:`str`): The message to display. + + .. versionadded:: 21.2 + """ + + __slots__ = ("message", "version") + + def __init__(self, version: str, message: str) -> None: + self.version: str = version + self.message: str = message + + def __str__(self) -> str: + """Returns a string representation of the warning, using :attr:`message` and + :attr:`version`. + + .. versionadded:: 21.2 + """ + return f"Deprecated since version {self.version}: {self.message}"