Merge branch 'master' into admonition-overhaul

This commit is contained in:
Harshil 2024-09-08 00:37:31 -04:00
commit f47df21dbf
No known key found for this signature in database
GPG key ID: 4AC061E441A1F727
160 changed files with 2064 additions and 1289 deletions

View file

@ -10,7 +10,7 @@ jobs:
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
strategy: strategy:
matrix: matrix:
python-version: [3.9] python-version: [3.10]
os: [ubuntu-latest] os: [ubuntu-latest]
fail-fast: False fail-fast: False
steps: steps:

View file

@ -1,17 +1,15 @@
name: Publish to PyPI name: Publish to PyPI
on: on:
# Run on any tag # manually trigger the workflow
push:
tags:
- '**'
# manually trigger the workflow - for testing only
workflow_dispatch: workflow_dispatch:
jobs: jobs:
build: build:
name: Build Distribution name: Build Distribution
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs:
TAG: ${{ steps.get_tag.outputs.TAG }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -29,11 +27,15 @@ jobs:
with: with:
name: python-package-distributions name: python-package-distributions
path: dist/ path: dist/
- name: Get Tag Name
id: get_tag
run: |
pip install .
TAG=$(python -c "from telegram import __version__; print(f'v{__version__}')")
echo "TAG=$TAG" >> $GITHUB_OUTPUT
publish-to-pypi: publish-to-pypi:
name: Publish to PyPI name: Publish to PyPI
# only publish to PyPI on tag pushes
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
needs: needs:
- build - build
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -52,42 +54,11 @@ jobs:
- name: Publish to PyPI - name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1 uses: pypa/gh-action-pypi-publish@release/v1
publish-to-test-pypi:
name: Publish to Test PyPI
needs:
- build
runs-on: ubuntu-latest
environment:
name: release_test_pypi
url: https://test.pypi.org/p/python-telegram-bot
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Publish to Test PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
compute-signatures: compute-signatures:
name: Compute SHA1 Sums and Sign with Sigstore name: Compute SHA1 Sums and Sign with Sigstore
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
- publish-to-pypi - publish-to-pypi
- publish-to-test-pypi
# run if either of the publishing jobs ran successfully
# see also:
# https://github.com/actions/runner/issues/491#issuecomment-850884422
if: |
always() && (
(needs.publish-to-pypi.result == 'success') ||
(needs.publish-to-test-pypi.result == 'success')
)
permissions: permissions:
id-token: write # IMPORTANT: mandatory for sigstore id-token: write # IMPORTANT: mandatory for sigstore
@ -106,7 +77,7 @@ jobs:
sha1sum $file > $file.sha1 sha1sum $file > $file.sha1
done done
- name: Sign the dists with Sigstore - name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@v2.1.1 uses: sigstore/gh-action-sigstore-python@v3.0.0
with: with:
inputs: >- inputs: >-
./dist/*.tar.gz ./dist/*.tar.gz
@ -120,13 +91,8 @@ jobs:
github-release: github-release:
name: Upload to GitHub Release name: Upload to GitHub Release
needs: needs:
- publish-to-pypi - build
- compute-signatures - compute-signatures
if: |
always() && (
(needs.publish-to-pypi.result == 'success') &&
(needs.compute-signatures.result == 'success')
)
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -142,63 +108,22 @@ jobs:
- name: Create GitHub Release - name: Create GitHub Release
env: env:
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}
# Create a GitHub Release for this tag. The description can be changed later, as for now TAG: ${{ needs.build.outputs.TAG }}
# Create a tag and a GitHub Release. The description can be changed later, as for now
# we don't define it through this workflow. # we don't define it through this workflow.
run: >- run: >-
gh release create gh release create
'${{ github.ref_name }}' '${{ env.TAG }}'
--repo '${{ github.repository }}' --repo '${{ github.repository }}'
--generate-notes --generate-notes
- name: Upload artifact signatures to GitHub Release - name: Upload artifact signatures to GitHub Release
env: env:
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}
TAG: ${{ needs.build.outputs.TAG }}
# Upload to GitHub Release using the `gh` CLI. # Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the # `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates. # sigstore-produced signatures and certificates.
run: >- run: >-
gh release upload gh release upload
'${{ github.ref_name }}' dist/** '${{ env.TAG }}' dist/**
--repo '${{ github.repository }}'
github-test-release:
name: Upload to GitHub Release Draft
needs:
- publish-to-test-pypi
- compute-signatures
if: |
always() && (
(needs.publish-to-test-pypi.result == 'success') &&
(needs.compute-signatures.result == 'success')
)
runs-on: ubuntu-latest
permissions:
contents: write # IMPORTANT: mandatory for making GitHub Releases
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions-and-signatures
path: dist/
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
# Create a GitHub Release *draft*. The description can be changed later, as for now
# we don't define it through this workflow.
run: >-
gh release create
'${{ github.ref_name }}'
--repo '${{ github.repository }}'
--generate-notes
--draft
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
# Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates.
run: >-
gh release upload
'${{ github.ref_name }}' dist/**
--repo '${{ github.repository }}' --repo '${{ github.repository }}'

132
.github/workflows/release_test_pypi.yml vendored Normal file
View file

@ -0,0 +1,132 @@
name: Publish to Test PyPI
on:
# manually trigger the workflow
workflow_dispatch:
jobs:
build:
name: Build Distribution
runs-on: ubuntu-latest
outputs:
TAG: ${{ steps.get_tag.outputs.TAG }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install pypa/build
run: >-
python3 -m pip install build --user
- name: Build a binary wheel and a source tarball
run: python3 -m build
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Get Tag Name
id: get_tag
run: |
pip install .
TAG=$(python -c "from telegram import __version__; print(f'v{__version__}')")
echo "TAG=$TAG" >> $GITHUB_OUTPUT
publish-to-test-pypi:
name: Publish to Test PyPI
needs:
- build
runs-on: ubuntu-latest
environment:
name: release_test_pypi
url: https://test.pypi.org/p/python-telegram-bot
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Publish to Test PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
compute-signatures:
name: Compute SHA1 Sums and Sign with Sigstore
runs-on: ubuntu-latest
needs:
- publish-to-test-pypi
permissions:
id-token: write # IMPORTANT: mandatory for sigstore
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Compute SHA1 Sums
run: |
# Compute SHA1 sum of the distribution packages and save it to a file with the same name,
# but with .sha1 extension
for file in dist/*; do
sha1sum $file > $file.sha1
done
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@v3.0.0
with:
inputs: >-
./dist/*.tar.gz
./dist/*.whl
- name: Store the distribution packages and signatures
uses: actions/upload-artifact@v4
with:
name: python-package-distributions-and-signatures
path: dist/
github-test-release:
name: Upload to GitHub Release Draft
needs:
- build
- compute-signatures
runs-on: ubuntu-latest
permissions:
contents: write # IMPORTANT: mandatory for making GitHub Releases
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions-and-signatures
path: dist/
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
TAG: ${{ needs.build.outputs.TAG }}
# Create a GitHub Release *draft*. The description can be changed later, as for now
# we don't define it through this workflow.
run: >-
gh release create
'${{ env.TAG }}'
--repo '${{ github.repository }}'
--generate-notes
--draft
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
TAG: ${{ needs.build.outputs.TAG }}
# Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates.
run: >-
gh release upload
'${{ env.TAG }}' dist/**
--repo '${{ github.repository }}'

View file

@ -4,6 +4,58 @@
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 <https://t.me/pythontelegrambotchannel>`_.
Major Changes
-------------
- Full Support for Bot API 7.9 (:pr:`4429`)
- Full Support for Bot API 7.8 (:pr:`4408`)
New Features
------------
- Add ``MessageEntity.shift_entities`` and ``MessageEntity.concatenate`` (:pr:`4376` closes :issue:`4372`)
- Add Parameter ``game_pattern`` to ``CallbackQueryHandler`` (:pr:`4353` by `jainamoswal <https://github.com/jainamoswal>`_ closes :issue:`4269`)
- Add Parameter ``read_file_handle`` to ``InputFile`` (:pr:`4388` closes :issue:`4339`)
Documentation Improvements
--------------------------
- Bugfix for "Available In" Admonitions (:pr:`4413`)
- Documentation Improvements (:pr:`4400` closes :issue:`4446`, :pr:`4448` by `Palaptin <https://github.com/Palaptin>`_)
- Document Return Types of ``RequestData`` Members (:pr:`4396`)
- Add Introductory Paragraphs to Telegram Types Subsections (:pr:`4389` by `mohdyusuf2312 <https://github.com/mohdyusuf2312>`_ closes :issue:`4380`)
- Start Adapting to RTD Addons (:pr:`4386`)
Minor and Internal Changes
---------------------------
- Remove Surplus Logging from ``Updater`` Network Loop (:pr:`4432` by `MartinHjelmare <https://github.com/MartinHjelmare>`_)
- Add Internal Constants for Encodings (:pr:`4378` by `elpekenin <https://github.com/elpekenin>`_)
- Improve PyPI Automation (:pr:`4375` closes :issue:`4373`)
- Update Test Suite to New Test Channel Setup (:pr:`4435`)
- Improve Fixture Usage in ``test_message.py`` (:pr:`4431` by `Palaptin <https://github.com/Palaptin>`_)
- Update Python 3.13 Test Suite to RC1 (:pr:`4415`)
- Bump ``ruff`` and Add New Rules (:pr:`4416`)
Dependency Updates
------------------
- Update ``cachetools`` requirement from <5.5.0,>=5.3.3 to >=5.3.3,<5.6.0 (:pr:`4437`)
- Bump ``sphinx`` from 7.4.7 to 8.0.2 and ``furo`` from 2024.7.18 to 2024.8.6 (:pr:`4412`)
- Bump ``test-summary/action`` from 2.3 to 2.4 (:pr:`4410`)
- Bump ``pytest`` from 8.2.2 to 8.3.2 (:pr:`4403`)
- Bump ``dependabot/fetch-metadata`` from 2.1.0 to 2.2.0 (:pr:`4411`)
- Update ``cachetools`` requirement from ~=5.3.3 to >=5.3.3,<5.5.0 (:pr:`4390`)
- Bump ``sphinx`` from 7.3.7 to 7.4.7 (:pr:`4395`)
- Bump ``furo`` from 2024.5.6 to 2024.7.18 (:pr:`4392`)
Version 21.4 Version 21.4
============ ============

View file

@ -11,7 +11,7 @@
:target: https://pypi.org/project/python-telegram-bot/ :target: https://pypi.org/project/python-telegram-bot/
:alt: Supported Python versions :alt: Supported Python versions
.. image:: https://img.shields.io/badge/Bot%20API-7.8-blue?logo=telegram .. image:: https://img.shields.io/badge/Bot%20API-7.9-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog :target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API version :alt: Supported Bot API version
@ -81,7 +81,7 @@ After installing_ the library, be sure to check out the section on `working with
Telegram API support Telegram API support
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
All types and methods of the Telegram Bot API **7.8** are natively supported by this library. 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 <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Bot-API-Forward-Compatibility>`_. In addition, Bot API functionality not yet natively included can still be used as described `in our wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Bot-API-Forward-Compatibility>`_.
Notable Features Notable Features
@ -119,7 +119,7 @@ Verifying Releases
To enable you to verify that a release file that you downloaded was indeed provided by the ``python-telegram-bot`` team, we have taken the following measures. To enable you to verify that a release file that you downloaded was indeed provided by the ``python-telegram-bot`` team, we have taken the following measures.
Starting with v21.4, all releases are signed via `sigstore <https://sigstore.dev>`_. Starting with v21.4, all releases are signed via `sigstore <https://www.sigstore.dev>`_.
The corresponding signature files are uploaded to the `GitHub releases page`_. The corresponding signature files are uploaded to the `GitHub releases page`_.
To verify the signature, please install the `sigstore Python client <https://pypi.org/project/sigstore/>`_ and follow the instructions for `verifying signatures from GitHub Actions <https://github.com/sigstore/sigstore-python#signatures-from-github-actions>`_. As input for the ``--repository`` parameter, please use the value ``python-telegram-bot/python-telegram-bot``. To verify the signature, please install the `sigstore Python client <https://pypi.org/project/sigstore/>`_ and follow the instructions for `verifying signatures from GitHub Actions <https://github.com/sigstore/sigstore-python#signatures-from-github-actions>`_. As input for the ``--repository`` parameter, please use the value ``python-telegram-bot/python-telegram-bot``.
@ -157,7 +157,7 @@ PTB can be installed with optional dependencies:
* ``pip install "python-telegram-bot[http2]"`` installs `httpx[http2] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to use HTTP/2. * ``pip install "python-telegram-bot[http2]"`` installs `httpx[http2] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to use HTTP/2.
* ``pip install "python-telegram-bot[rate-limiter]"`` installs `aiolimiter~=1.1.0 <https://aiolimiter.readthedocs.io/en/stable/>`_. Use this, if you want to use ``telegram.ext.AIORateLimiter``. * ``pip install "python-telegram-bot[rate-limiter]"`` installs `aiolimiter~=1.1.0 <https://aiolimiter.readthedocs.io/en/stable/>`_. Use this, if you want to use ``telegram.ext.AIORateLimiter``.
* ``pip install "python-telegram-bot[webhooks]"`` installs the `tornado~=6.4 <https://www.tornadoweb.org/en/stable/>`_ library. Use this, if you want to use ``telegram.ext.Updater.start_webhook``/``telegram.ext.Application.run_webhook``. * ``pip install "python-telegram-bot[webhooks]"`` installs the `tornado~=6.4 <https://www.tornadoweb.org/en/stable/>`_ library. Use this, if you want to use ``telegram.ext.Updater.start_webhook``/``telegram.ext.Application.run_webhook``.
* ``pip install "python-telegram-bot[callback-data]"`` installs the `cachetools>=5.3.3,<5.5.0 <https://cachetools.readthedocs.io/en/latest/>`_ library. Use this, if you want to use `arbitrary callback_data <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Arbitrary-callback_data>`_. * ``pip install "python-telegram-bot[callback-data]"`` installs the `cachetools>=5.3.3,<5.6.0 <https://cachetools.readthedocs.io/en/latest/>`_ library. Use this, if you want to use `arbitrary callback_data <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Arbitrary-callback_data>`_.
* ``pip install "python-telegram-bot[job-queue]"`` installs the `APScheduler~=3.10.4 <https://apscheduler.readthedocs.io/en/3.x/>`_ library and enforces `pytz>=2018.6 <https://pypi.org/project/pytz/>`_, where ``pytz`` is a dependency of ``APScheduler``. Use this, if you want to use the ``telegram.ext.JobQueue``. * ``pip install "python-telegram-bot[job-queue]"`` installs the `APScheduler~=3.10.4 <https://apscheduler.readthedocs.io/en/3.x/>`_ library and enforces `pytz>=2018.6 <https://pypi.org/project/pytz/>`_, where ``pytz`` is a dependency of ``APScheduler``. Use this, if you want to use the ``telegram.ext.JobQueue``.
To install multiple optional dependencies, separate them by commas, e.g. ``pip install "python-telegram-bot[socks,webhooks]"``. To install multiple optional dependencies, separate them by commas, e.g. ``pip install "python-telegram-bot[socks,webhooks]"``.

View file

@ -130,6 +130,7 @@ Available Types
telegram.reactiontype telegram.reactiontype
telegram.reactiontypecustomemoji telegram.reactiontypecustomemoji
telegram.reactiontypeemoji telegram.reactiontypeemoji
telegram.reactiontypepaid
telegram.replykeyboardmarkup telegram.replykeyboardmarkup
telegram.replykeyboardremove telegram.replykeyboardremove
telegram.replyparameters telegram.replyparameters

View file

@ -10,7 +10,7 @@ Your bot can offer users **HTML5 games** to play solo or to compete against each
* If you send the game message without any buttons, it will automatically have a 'Play ``GameName``' button. When this button is pressed, your bot gets a :class:`telegram.CallbackQuery` with the ``game_short_name`` of the requested game. You provide the correct URL for this particular user and the app opens the game in the in-app browser. * If you send the game message without any buttons, it will automatically have a 'Play ``GameName``' button. When this button is pressed, your bot gets a :class:`telegram.CallbackQuery` with the ``game_short_name`` of the requested game. You provide the correct URL for this particular user and the app opens the game in the in-app browser.
* You can manually add multiple buttons to your game message. Please note that the first button in the first row **must always** launch the game, using the field ``callback_game`` in :class:`telegram.InlineKeyboardButton`. You can add extra buttons according to taste: e.g., for a description of the rules, or to open the game's official community. * You can manually add multiple buttons to your game message. Please note that the first button in the first row **must always** launch the game, using the field ``callback_game`` in :class:`telegram.InlineKeyboardButton`. You can add extra buttons according to taste: e.g., for a description of the rules, or to open the game's official community.
* To make your game more attractive, you can upload a GIF animation that demonstrates the game to the users via `BotFather <https://t.me/botfather>`_ (see `Lumberjack <https://t.me/gamebot?game=lumberjack>`_ for example). * To make your game more attractive, you can upload a GIF animation that demonstrates the game to the users via `BotFather <https://t.me/botfather>`_ (see `Lumberjack <https://t.me/gamebot?game=lumberjack>`_ for example).
* A game message will also display high scores for the current chat. Use :meth:`~telegram.Bot.setGameScore` to post high scores to the chat with the game, optionally add the :paramref:`~telegram.Bot.set_game_score.disable_edit_message` parameter if you don't want to automatically update the message with the current scoreboard. * A game message will also display high scores for the current chat. Use :meth:`~telegram.Bot.setGameScore` to post high scores to the chat with the game, add the :paramref:`~telegram.Bot.set_game_score.disable_edit_message` parameter to disable automatic update of the message with the current scoreboard.
* Use :meth:`~telegram.Bot.getGameHighScores` to get data for in-game high score tables. * Use :meth:`~telegram.Bot.getGameHighScores` to get data for in-game high score tables.
* You can also add an extra sharing button for users to share their best score to different chats. * You can also add an extra sharing button for users to share their best score to different chats.
* For examples of what can be done using this new stuff, check the `@gamebot <https://t.me/gamebot>`_ and `@gamee <https://t.me/gamee>`_ bots. * For examples of what can be done using this new stuff, check the `@gamebot <https://t.me/gamebot>`_ and `@gamee <https://t.me/gamee>`_ bots.

View file

@ -0,0 +1,6 @@
ReactionTypePaid
================
.. autoclass:: telegram.ReactionTypePaid
:members:
:show-inheritance:

View file

@ -60,10 +60,12 @@
.. |removed_thumb_note| replace:: Removed the deprecated argument and attribute ``thumb``. .. |removed_thumb_note| replace:: Removed the deprecated argument and attribute ``thumb``.
.. |removed_thumb_url_note| replace:: Removed the deprecated argument and attribute ``thumb_url``. .. |removed_thumb_url_note| replace:: Removed the deprecated argument and attribute ``thumb_url`` which made thumbnail_url mandatory.
.. |removed_thumb_wildcard_note| replace:: Removed the deprecated arguments and attributes ``thumb_*``. .. |removed_thumb_wildcard_note| replace:: Removed the deprecated arguments and attributes ``thumb_*``.
.. |thumbnail_url_mandatory| replace:: Removal of the deprecated argument ``thumb_url`` made ``thumbnail_url`` mandatory.
.. |async_context_manager| replace:: Asynchronous context manager which .. |async_context_manager| replace:: Asynchronous context manager which
.. |reply_parameters| replace:: Description of the message to reply to. .. |reply_parameters| replace:: Description of the message to reply to.

View file

@ -67,7 +67,7 @@ all = [
] ]
callback-data = [ callback-data = [
# Cachetools doesn't have a strict stability policy. Let's be cautious for now. # Cachetools doesn't have a strict stability policy. Let's be cautious for now.
"cachetools>=5.3.3,<5.5.0", "cachetools>=5.3.3,<5.6.0",
] ]
ext = [ ext = [
"python-telegram-bot[callback-data,job-queue,rate-limiter,webhooks]", "python-telegram-bot[callback-data,job-queue,rate-limiter,webhooks]",

View file

@ -204,6 +204,7 @@ __all__ = (
"ReactionType", "ReactionType",
"ReactionTypeCustomEmoji", "ReactionTypeCustomEmoji",
"ReactionTypeEmoji", "ReactionTypeEmoji",
"ReactionTypePaid",
"RefundedPayment", "RefundedPayment",
"ReplyKeyboardMarkup", "ReplyKeyboardMarkup",
"ReplyKeyboardRemove", "ReplyKeyboardRemove",
@ -467,7 +468,13 @@ from ._payment.stars import (
from ._payment.successfulpayment import SuccessfulPayment from ._payment.successfulpayment import SuccessfulPayment
from ._poll import InputPollOption, Poll, PollAnswer, PollOption from ._poll import InputPollOption, Poll, PollAnswer, PollOption
from ._proximityalerttriggered import ProximityAlertTriggered from ._proximityalerttriggered import ProximityAlertTriggered
from ._reaction import ReactionCount, ReactionType, ReactionTypeCustomEmoji, ReactionTypeEmoji from ._reaction import (
ReactionCount,
ReactionType,
ReactionTypeCustomEmoji,
ReactionTypeEmoji,
ReactionTypePaid,
)
from ._reply import ExternalReplyInfo, ReplyParameters, TextQuote from ._reply import ExternalReplyInfo, ReplyParameters, TextQuote
from ._replykeyboardmarkup import ReplyKeyboardMarkup from ._replykeyboardmarkup import ReplyKeyboardMarkup
from ._replykeyboardremove import ReplyKeyboardRemove from ._replykeyboardremove import ReplyKeyboardRemove

View file

@ -18,6 +18,7 @@
# You should have received a copy of the GNU Lesser Public License # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Bot.""" """This module contains an object that represents a Telegram Bot."""
import asyncio import asyncio
import contextlib import contextlib
import copy import copy
@ -4427,18 +4428,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
If you're having any trouble setting up webhooks, please check out this `guide to If you're having any trouble setting up webhooks, please check out this `guide to
Webhooks`_. Webhooks`_.
Note:
1. You will not be able to receive updates using :meth:`get_updates` for long as an
outgoing webhook is set up.
2. To use a self-signed certificate, you need to upload your public key certificate
using certificate parameter. Please upload as InputFile, sending a String will not
work.
3. Ports currently supported for Webhooks:
:attr:`telegram.constants.SUPPORTED_WEBHOOK_PORTS`.
If you're having any trouble setting up webhooks, please check out this `guide to
Webhooks`_.
.. seealso:: :meth:`telegram.ext.Application.run_webhook`, .. seealso:: :meth:`telegram.ext.Application.run_webhook`,
:meth:`telegram.ext.Updater.start_webhook` :meth:`telegram.ext.Updater.start_webhook`
@ -5019,7 +5008,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
payload (:obj:`str`): Bot-defined invoice payload. payload (:obj:`str`): Bot-defined invoice payload.
:tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`- :tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`-
:tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be :tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be
displayed to the user, use for your internal processes. displayed to the user, use it for your internal processes.
provider_token (:obj:`str`): Payments provider token, obtained via provider_token (:obj:`str`): Payments provider token, obtained via
`@BotFather <https://t.me/BotFather>`_. Pass an empty string for payments in `@BotFather <https://t.me/BotFather>`_. Pass an empty string for payments in
|tg_stars|. |tg_stars|.
@ -5785,10 +5774,10 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
Args: Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
invite_link (:obj:`str` | :obj:`telegram.ChatInviteLink`): The invite link to edit. invite_link (:obj:`str` | :class:`telegram.ChatInviteLink`): The invite link to edit.
.. versionchanged:: 20.0 .. versionchanged:: 20.0
Now also accepts :obj:`telegram.ChatInviteLink` instances. Now also accepts :class:`telegram.ChatInviteLink` instances.
expire_date (:obj:`int` | :obj:`datetime.datetime`, optional): Date when the link will expire_date (:obj:`int` | :obj:`datetime.datetime`, optional): Date when the link will
expire. expire.
For timezone naive :obj:`datetime.datetime` objects, the default timezone of the For timezone naive :obj:`datetime.datetime` objects, the default timezone of the
@ -5857,10 +5846,10 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
Args: Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
invite_link (:obj:`str` | :obj:`telegram.ChatInviteLink`): The invite link to revoke. invite_link (:obj:`str` | :class:`telegram.ChatInviteLink`): The invite link to revoke.
.. versionchanged:: 20.0 .. versionchanged:: 20.0
Now also accepts :obj:`telegram.ChatInviteLink` instances. Now also accepts :class:`telegram.ChatInviteLink` instances.
Returns: Returns:
:class:`telegram.ChatInviteLink` :class:`telegram.ChatInviteLink`
@ -6156,7 +6145,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
business_connection_id (:obj:`str`, optional): Unique identifier of the business business_connection_id (:obj:`str`, optional): Unique identifier of the business
connection on behalf of which the message will be pinned. connection on behalf of which the message will be pinned.
.. versionadded:: NEXT.VERSION .. versionadded:: 21.5
Returns: Returns:
:obj:`bool`: On success, :obj:`True` is returned. :obj:`bool`: On success, :obj:`True` is returned.
@ -6209,7 +6198,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
business_connection_id (:obj:`str`, optional): Unique identifier of the business business_connection_id (:obj:`str`, optional): Unique identifier of the business
connection on behalf of which the message will be unpinned. connection on behalf of which the message will be unpinned.
.. versionadded:: NEXT.VERSION .. versionadded:: 21.5
Returns: Returns:
:obj:`bool`: On success, :obj:`True` is returned. :obj:`bool`: On success, :obj:`True` is returned.
@ -7402,8 +7391,9 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
.. versionadded:: 20.0 .. versionadded:: 20.0
Args: Args:
rights (:obj:`telegram.ChatAdministratorRights`, optional): A rights (:class:`telegram.ChatAdministratorRights`, optional): A
:obj:`telegram.ChatAdministratorRights` object describing new default administrator :class:`telegram.ChatAdministratorRights` object describing new default
administrator
rights. If not specified, the default administrator rights will be cleared. rights. If not specified, the default administrator rights will be cleared.
for_channels (:obj:`bool`, optional): Pass :obj:`True` to change the default for_channels (:obj:`bool`, optional): Pass :obj:`True` to change the default
administrator rights of the bot in channels. Otherwise, the default administrator administrator rights of the bot in channels. Otherwise, the default administrator
@ -7413,7 +7403,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
:obj:`bool`: Returns :obj:`True` on success. :obj:`bool`: Returns :obj:`True` on success.
Raises: Raises:
:obj:`telegram.error.TelegramError` :exc:`telegram.error.TelegramError`
""" """
data: JSONDict = {"rights": rights, "for_channels": for_channels} data: JSONDict = {"rights": rights, "for_channels": for_channels}
@ -7979,7 +7969,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
payload (:obj:`str`): Bot-defined invoice payload. payload (:obj:`str`): Bot-defined invoice payload.
:tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`- :tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`-
:tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be :tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be
displayed to the user, use for your internal processes. displayed to the user, use it for your internal processes.
provider_token (:obj:`str`): Payments provider token, obtained via provider_token (:obj:`str`): Payments provider token, obtained via
`@BotFather <https://t.me/BotFather>`_. Pass an empty string for payments in `@BotFather <https://t.me/BotFather>`_. Pass an empty string for payments in
|tg_stars|. |tg_stars|.
@ -8179,7 +8169,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
) -> bool: ) -> bool:
""" """
Use this method to edit name and icon of a topic in a forum supergroup chat. The bot must Use this method to edit name and icon of a topic in a forum supergroup chat. The bot must
be an administrator in the chat for this to work and must have be an administrator in the chat for this to work and must have the
:paramref:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights, :paramref:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights,
unless it is the creator of the topic. unless it is the creator of the topic.
@ -8447,7 +8437,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
) -> bool: ) -> bool:
""" """
Use this method to edit the name of the 'General' topic in a forum supergroup chat. The bot Use this method to edit the name of the 'General' topic in a forum supergroup chat. The bot
must be an administrator in the chat for this to work and must have must be an administrator in the chat for this to work and must have the
:attr:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights. :attr:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights.
.. versionadded:: 20.0 .. versionadded:: 20.0
@ -8946,7 +8936,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
""" """
Use this method to change the chosen reactions on a message. Service messages can't be 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 reacted to. Automatically forwarded messages from a channel to its discussion group have
the same available reactions as messages in the channel. the same available reactions as messages in the channel. Bots can't use paid reactions.
.. versionadded:: 20.8 .. versionadded:: 20.8
@ -8959,7 +8949,8 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
:class:`telegram.ReactionType` | :obj:`str`, optional): A list of reaction :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 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 one reaction per message. A custom emoji reaction can be used if it is either
already present on the message or explicitly allowed by chat administrators. already present on the message or explicitly allowed by chat administrators. Paid
reactions can't be used by bots.
Tip: Tip:
Passed :obj:`str` values will be converted to either Passed :obj:`str` values will be converted to either
@ -9076,7 +9067,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
user_id (:obj:`int`): User identifier of the sticker set owner. user_id (:obj:`int`): User identifier of the sticker set owner.
name (:obj:`str`): Sticker set name. name (:obj:`str`): Sticker set name.
old_sticker (:obj:`str`): File identifier of the replaced sticker. old_sticker (:obj:`str`): File identifier of the replaced sticker.
sticker (:obj:`telegram.InputSticker`): An object with information about the added sticker (:class:`telegram.InputSticker`): An object with information about the added
sticker. If exactly the same sticker had already been added to the set, then the sticker. If exactly the same sticker had already been added to the set, then the
set remains unchanged. set remains unchanged.
@ -9201,6 +9192,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
protect_content: ODVInput[bool] = DEFAULT_NONE, protect_content: ODVInput[bool] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None, reply_parameters: Optional["ReplyParameters"] = None,
reply_markup: Optional[ReplyMarkup] = None, reply_markup: Optional[ReplyMarkup] = None,
business_connection_id: Optional[str] = None,
*, *,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None, reply_to_message_id: Optional[int] = None,
@ -9210,12 +9202,14 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
pool_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None, api_kwargs: Optional[JSONDict] = None,
) -> Message: ) -> Message:
"""Use this method to send paid media to channel chats. """Use this method to send paid media.
.. versionadded:: 21.4 .. versionadded:: 21.4
Args: Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| If the chat is a channel, all
Telegram Star proceeds from this media will be credited to the chat's balance.
Otherwise, they will be credited to the bot's balance.
star_count (:obj:`int`): The number of Telegram Stars that must be paid to buy access star_count (:obj:`int`): The number of Telegram Stars that must be paid to buy access
to the media. to the media.
media (Sequence[:class:`telegram.InputPaidMedia`]): A list describing the media to be media (Sequence[:class:`telegram.InputPaidMedia`]): A list describing the media to be
@ -9233,6 +9227,9 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
:class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional): :class:`ReplyKeyboardRemove` | :class:`ForceReply`, optional):
Additional interface options. An object for an inline keyboard, custom reply 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. 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: Keyword Args:
allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply|
@ -9274,8 +9271,122 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
connect_timeout=connect_timeout, connect_timeout=connect_timeout,
pool_timeout=pool_timeout, pool_timeout=pool_timeout,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
business_connection_id=business_connection_id,
) )
async def create_chat_subscription_invite_link(
self,
chat_id: Union[str, int],
subscription_period: int,
subscription_price: int,
name: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> ChatInviteLink:
"""
Use this method to create a `subscription invite link <https://telegram.org/blog/\
superchannels-star-reactions-subscriptions#star-subscriptions>`_ for a channel chat.
The bot must have the :attr:`~telegram.ChatPermissions.can_invite_users` administrator
right. The link can be edited using the :meth:`edit_chat_subscription_invite_link` or
revoked using the :meth:`revoke_chat_invite_link`.
.. versionadded:: 21.5
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
subscription_period (:obj:`int`): The number of seconds the subscription will be
active for before the next payment. Currently, it must always be
:tg-const:`telegram.constants.ChatSubscriptionLimit.SUBSCRIPTION_PERIOD` (30 days).
subscription_price (:obj:`int`): The number of Telegram Stars a user must pay initially
and after each subsequent subscription period to be a member of the chat;
:tg-const:`telegram.constants.ChatSubscriptionLimit.MIN_PRICE`-
:tg-const:`telegram.constants.ChatSubscriptionLimit.MAX_PRICE`.
name (:obj:`str`, optional): Invite link name;
0-:tg-const:`telegram.constants.ChatInviteLinkLimit.NAME_LENGTH` characters.
Returns:
:class:`telegram.ChatInviteLink`
Raises:
:class:`telegram.error.TelegramError`
"""
data: JSONDict = {
"chat_id": chat_id,
"subscription_period": subscription_period,
"subscription_price": subscription_price,
"name": name,
}
result = await self._post(
"createChatSubscriptionInviteLink",
data,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
)
return ChatInviteLink.de_json(result, self) # type: ignore[return-value]
async def edit_chat_subscription_invite_link(
self,
chat_id: Union[str, int],
invite_link: Union[str, "ChatInviteLink"],
name: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> ChatInviteLink:
"""
Use this method to edit a subscription invite link created by the bot. The bot must have
:attr:`telegram.ChatPermissions.can_invite_users` administrator right.
.. versionadded:: 21.5
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
invite_link (:obj:`str` | :obj:`telegram.ChatInviteLink`): The invite link to edit.
name (:obj:`str`, optional): Invite link name;
0-:tg-const:`telegram.constants.ChatInviteLinkLimit.NAME_LENGTH` characters.
Tip:
Omitting this argument removes the name of the invite link.
Returns:
:class:`telegram.ChatInviteLink`
Raises:
:class:`telegram.error.TelegramError`
"""
link = invite_link.invite_link if isinstance(invite_link, ChatInviteLink) else invite_link
data: JSONDict = {
"chat_id": chat_id,
"invite_link": link,
"name": name,
}
result = await self._post(
"editChatSubscriptionInviteLink",
data,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
)
return ChatInviteLink.de_json(result, self) # type: ignore[return-value]
def to_dict(self, recursive: bool = True) -> JSONDict: # noqa: ARG002 def to_dict(self, recursive: bool = True) -> JSONDict: # noqa: ARG002
"""See :meth:`telegram.TelegramObject.to_dict`.""" """See :meth:`telegram.TelegramObject.to_dict`."""
data: JSONDict = {"id": self.id, "username": self.username, "first_name": self.first_name} data: JSONDict = {"id": self.id, "username": self.username, "first_name": self.first_name}
@ -9532,3 +9643,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
"""Alias for :meth:`get_star_transactions`""" """Alias for :meth:`get_star_transactions`"""
sendPaidMedia = send_paid_media sendPaidMedia = send_paid_media
"""Alias for :meth:`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`"""

View file

@ -2666,6 +2666,81 @@ class _ChatBase(TelegramObject):
api_kwargs=api_kwargs, 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( async def approve_join_request(
self, self,
user_id: int, user_id: int,
@ -3274,6 +3349,7 @@ class _ChatBase(TelegramObject):
protect_content: ODVInput[bool] = DEFAULT_NONE, protect_content: ODVInput[bool] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None, reply_parameters: Optional["ReplyParameters"] = None,
reply_markup: Optional[ReplyMarkup] = None, reply_markup: Optional[ReplyMarkup] = None,
business_connection_id: Optional[str] = None,
*, *,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None, reply_to_message_id: Optional[int] = None,
@ -3314,6 +3390,7 @@ class _ChatBase(TelegramObject):
connect_timeout=connect_timeout, connect_timeout=connect_timeout,
pool_timeout=pool_timeout, pool_timeout=pool_timeout,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
business_connection_id=business_connection_id,
) )

View file

@ -307,7 +307,7 @@ class BackgroundTypeFill(BackgroundType):
.. versionadded:: 21.2 .. versionadded:: 21.2
Args: Args:
fill (:obj:`telegram.BackgroundFill`): The background fill. fill (:class:`telegram.BackgroundFill`): The background fill.
dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a
percentage; percentage;
0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`.
@ -315,7 +315,7 @@ class BackgroundTypeFill(BackgroundType):
Attributes: Attributes:
type (:obj:`str`): Type of the background. Always type (:obj:`str`): Type of the background. Always
:attr:`~telegram.BackgroundType.FILL`. :attr:`~telegram.BackgroundType.FILL`.
fill (:obj:`telegram.BackgroundFill`): The background fill. fill (:class:`telegram.BackgroundFill`): The background fill.
dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a
percentage; percentage;
0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`.
@ -349,7 +349,7 @@ class BackgroundTypeWallpaper(BackgroundType):
.. versionadded:: 21.2 .. versionadded:: 21.2
Args: Args:
document (:obj:`telegram.Document`): Document with the wallpaper document (:class:`telegram.Document`): Document with the wallpaper
dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a
percentage; percentage;
0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`.
@ -361,7 +361,7 @@ class BackgroundTypeWallpaper(BackgroundType):
Attributes: Attributes:
type (:obj:`str`): Type of the background. Always type (:obj:`str`): Type of the background. Always
:attr:`~telegram.BackgroundType.WALLPAPER`. :attr:`~telegram.BackgroundType.WALLPAPER`.
document (:obj:`telegram.Document`): Document with the wallpaper document (:class:`telegram.Document`): Document with the wallpaper
dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a
percentage; percentage;
0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`. 0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`.
@ -407,8 +407,8 @@ class BackgroundTypePattern(BackgroundType):
.. versionadded:: 21.2 .. versionadded:: 21.2
Args: Args:
document (:obj:`telegram.Document`): Document with the pattern. document (:class:`telegram.Document`): Document with the pattern.
fill (:obj:`telegram.BackgroundFill`): The background fill that is combined with fill (:class:`telegram.BackgroundFill`): The background fill that is combined with
the pattern. the pattern.
intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled
background; background;
@ -422,8 +422,8 @@ class BackgroundTypePattern(BackgroundType):
Attributes: Attributes:
type (:obj:`str`): Type of the background. Always type (:obj:`str`): Type of the background. Always
:attr:`~telegram.BackgroundType.PATTERN`. :attr:`~telegram.BackgroundType.PATTERN`.
document (:obj:`telegram.Document`): Document with the pattern. document (:class:`telegram.Document`): Document with the pattern.
fill (:obj:`telegram.BackgroundFill`): The background fill that is combined with fill (:class:`telegram.BackgroundFill`): The background fill that is combined with
the pattern. the pattern.
intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled
background; background;
@ -511,10 +511,10 @@ class ChatBackground(TelegramObject):
.. versionadded:: 21.2 .. versionadded:: 21.2
Args: Args:
type (:obj:`telegram.BackgroundType`): Type of the background. type (:class:`telegram.BackgroundType`): Type of the background.
Attributes: Attributes:
type (:obj:`telegram.BackgroundType`): Type of the background. type (:class:`telegram.BackgroundType`): Type of the background.
""" """
__slots__ = ("type",) __slots__ = ("type",)

View file

@ -78,7 +78,7 @@ class ChatFullInfo(_ChatBase):
#collectible-usernames>`_; for private chats, supergroups and channels. #collectible-usernames>`_; for private chats, supergroups and channels.
.. versionadded:: 20.0 .. versionadded:: 20.0
birthdate (:obj:`telegram.Birthdate`, optional): For private chats, birthdate (:class:`telegram.Birthdate`, optional): For private chats,
the date of birth of the user. the date of birth of the user.
.. versionadded:: 21.1 .. versionadded:: 21.1
@ -94,8 +94,8 @@ class ChatFullInfo(_ChatBase):
chats with business accounts, the opening hours of the business. chats with business accounts, the opening hours of the business.
.. versionadded:: 21.1 .. versionadded:: 21.1
personal_chat (:obj:`telegram.Chat`, optional): For private chats, the personal channel of personal_chat (:class:`telegram.Chat`, optional): For private chats, the personal channel
the user. of the user.
.. versionadded:: 21.1 .. versionadded:: 21.1
available_reactions (Sequence[:class:`telegram.ReactionType`], optional): List of available available_reactions (Sequence[:class:`telegram.ReactionType`], optional): List of available
@ -232,7 +232,7 @@ class ChatFullInfo(_ChatBase):
obtained via :meth:`~telegram.Bot.get_chat`. obtained via :meth:`~telegram.Bot.get_chat`.
.. versionadded:: 20.0 .. versionadded:: 20.0
birthdate (:obj:`telegram.Birthdate`): Optional. For private chats, birthdate (:class:`telegram.Birthdate`): Optional. For private chats,
the date of birth of the user. the date of birth of the user.
.. versionadded:: 21.1 .. versionadded:: 21.1
@ -248,8 +248,8 @@ class ChatFullInfo(_ChatBase):
chats with business accounts, the opening hours of the business. chats with business accounts, the opening hours of the business.
.. versionadded:: 21.1 .. versionadded:: 21.1
personal_chat (:obj:`telegram.Chat`): Optional. For private chats, the personal channel of personal_chat (:class:`telegram.Chat`): Optional. For private chats, the personal channel
the user. of the user.
.. versionadded:: 21.1 .. versionadded:: 21.1
available_reactions (Tuple[:class:`telegram.ReactionType`]): Optional. List of available available_reactions (Tuple[:class:`telegram.ReactionType`]): Optional. List of available

View file

@ -69,6 +69,16 @@ class ChatInviteLink(TelegramObject):
created using this link. created using this link.
.. versionadded:: 13.8 .. 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: Attributes:
invite_link (:obj:`str`): The invite link. If the link was created by another chat 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 ``''``. administrator, then the second part of the link will be replaced with ``''``.
@ -96,6 +106,15 @@ class ChatInviteLink(TelegramObject):
created using this link. created using this link.
.. versionadded:: 13.8 .. versionadded:: 13.8
subscription_period (:obj:`int`): Optional. The number of seconds the subscription will be
active for before the next payment.
.. versionadded:: 21.5
subscription_price (:obj:`int`): Optional. The amount of Telegram Stars a user must pay
initially and after each subsequent subscription period to be a member of the chat
using the link.
.. versionadded:: 21.5
""" """
@ -109,6 +128,8 @@ class ChatInviteLink(TelegramObject):
"member_limit", "member_limit",
"name", "name",
"pending_join_request_count", "pending_join_request_count",
"subscription_period",
"subscription_price",
) )
def __init__( def __init__(
@ -122,6 +143,8 @@ class ChatInviteLink(TelegramObject):
member_limit: Optional[int] = None, member_limit: Optional[int] = None,
name: Optional[str] = None, name: Optional[str] = None,
pending_join_request_count: Optional[int] = None, pending_join_request_count: Optional[int] = None,
subscription_period: Optional[int] = None,
subscription_price: Optional[int] = None,
*, *,
api_kwargs: Optional[JSONDict] = None, api_kwargs: Optional[JSONDict] = None,
): ):
@ -140,6 +163,9 @@ class ChatInviteLink(TelegramObject):
self.pending_join_request_count: Optional[int] = ( self.pending_join_request_count: Optional[int] = (
int(pending_join_request_count) if pending_join_request_count is not None else None 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._id_attrs = (
self.invite_link, self.invite_link,
self.creates_join_request, self.creates_join_request,

View file

@ -17,6 +17,7 @@
# You should have received a copy of the GNU Lesser Public License # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ChatMember.""" """This module contains an object that represents a Telegram ChatMember."""
import datetime import datetime
from typing import TYPE_CHECKING, Dict, Final, Optional, Type from typing import TYPE_CHECKING, Dict, Final, Optional, Type
@ -391,24 +392,34 @@ class ChatMemberMember(ChatMember):
Args: Args:
user (:class:`telegram.User`): Information about the user. 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: Attributes:
status (:obj:`str`): The member's status in the chat, status (:obj:`str`): The member's status in the chat,
always :tg-const:`telegram.ChatMember.MEMBER`. always :tg-const:`telegram.ChatMember.MEMBER`.
user (:class:`telegram.User`): Information about the user. user (:class:`telegram.User`): Information about the user.
until_date (:class:`datetime.datetime`): Optional. Date when the user's subscription will
expire.
.. versionadded:: 21.5
""" """
__slots__ = () __slots__ = ("until_date",)
def __init__( def __init__(
self, self,
user: User, user: User,
until_date: Optional[datetime.datetime] = None,
*, *,
api_kwargs: Optional[JSONDict] = None, api_kwargs: Optional[JSONDict] = None,
): ):
super().__init__(status=ChatMember.MEMBER, user=user, api_kwargs=api_kwargs) super().__init__(status=ChatMember.MEMBER, user=user, api_kwargs=api_kwargs)
self._freeze() with self._unfrozen():
self.until_date: Optional[datetime.datetime] = until_date
class ChatMemberRestricted(ChatMember): class ChatMemberRestricted(ChatMember):

View file

@ -78,7 +78,7 @@ class InputFile:
# here the file handle is already closed and the upload will fail # here the file handle is already closed and the upload will fail
await bot.send_document(chat_id, input_file) await bot.send_document(chat_id, input_file)
.. versionadded:: NEXT.VERSION .. versionadded:: 21.5
Attributes: Attributes:
@ -126,7 +126,7 @@ class InputFile:
def field_tuple(self) -> FieldTuple: def field_tuple(self) -> FieldTuple:
"""Field tuple representing the contents of the file for upload to the Telegram servers. """Field tuple representing the contents of the file for upload to the Telegram servers.
.. versionchanged:: NEXT.VERSION .. versionchanged:: 21.5
Content may now be a file handle. Content may now be a file handle.
Returns: Returns:

View file

@ -49,7 +49,7 @@ class InputSticker(TelegramObject):
:tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI` - :tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI` -
:tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with the :tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with the
sticker. sticker.
mask_position (:obj:`telegram.MaskPosition`, optional): Position where the mask should be mask_position (:class:`telegram.MaskPosition`, optional): Position where the mask should be
placed on faces. For ":tg-const:`telegram.constants.StickerType.MASK`" stickers only. placed on faces. For ":tg-const:`telegram.constants.StickerType.MASK`" stickers only.
keywords (Sequence[:obj:`str`], optional): Sequence of keywords (Sequence[:obj:`str`], optional): Sequence of
0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords 0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords
@ -71,7 +71,7 @@ class InputSticker(TelegramObject):
:tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI` - :tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI` -
:tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with the :tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with the
sticker. sticker.
mask_position (:obj:`telegram.MaskPosition`): Optional. Position where the mask should be mask_position (:class:`telegram.MaskPosition`): Optional. Position where the mask should be
placed on faces. For ":tg-const:`telegram.constants.StickerType.MASK`" stickers only. placed on faces. For ":tg-const:`telegram.constants.StickerType.MASK`" stickers only.
keywords (Tuple[:obj:`str`]): Optional. Tuple of keywords (Tuple[:obj:`str`]): Optional. Tuple of
0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords 0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords

View file

@ -126,7 +126,7 @@ class Giveaway(TelegramObject):
def de_json( def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["Giveaway"]: ) -> Optional["Giveaway"]:
"""See :obj:`telegram.TelegramObject.de_json`.""" """See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data) data = cls._parse_data(data)
if data is None: if data is None:
@ -262,7 +262,7 @@ class GiveawayWinners(TelegramObject):
def de_json( def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["GiveawayWinners"]: ) -> Optional["GiveawayWinners"]:
"""See :obj:`telegram.TelegramObject.de_json`.""" """See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data) data = cls._parse_data(data)
if data is None: if data is None:
@ -330,7 +330,7 @@ class GiveawayCompleted(TelegramObject):
def de_json( def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["GiveawayCompleted"]: ) -> Optional["GiveawayCompleted"]:
"""See :obj:`telegram.TelegramObject.de_json`.""" """See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data) data = cls._parse_data(data)
if data is None: if data is None:

View file

@ -99,7 +99,7 @@ class InlineKeyboardButton(TelegramObject):
.. seealso:: :wiki:`Arbitrary callback_data <Arbitrary-callback_data>` .. seealso:: :wiki:`Arbitrary callback_data <Arbitrary-callback_data>`
web_app (:obj:`telegram.WebAppInfo`, optional): Description of the `Web App web_app (:class:`telegram.WebAppInfo`, optional): Description of the `Web App
<https://core.telegram.org/bots/webapps>`_ that will be launched when the user presses <https://core.telegram.org/bots/webapps>`_ that will be launched when the user presses
the button. The Web App will be able to send an arbitrary message on behalf of the user 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 using the method :meth:`~telegram.Bot.answer_web_app_query`. Available only in
@ -107,16 +107,14 @@ class InlineKeyboardButton(TelegramObject):
a Telegram Business account. a Telegram Business account.
.. versionadded:: 20.0 .. versionadded:: 20.0
switch_inline_query (:obj:`str`, optional): If set, pressing the button will insert the switch_inline_query (:obj:`str`, optional): If set, pressing the button will prompt the
bot's username and the specified inline query in the current chat's input field. May be user to select one of their chats, open that chat and insert the bot's username and the
empty, in which case only the bot's username will be inserted. 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
This offers a quick way for the user to open your bot in inline mode in the same chat - Business account.
good for selecting something from multiple options. Not supported in channels and for
messages sent on behalf of a Telegram Business account.
Tip: Tip:
This is similar to the new parameter :paramref:`switch_inline_query_chosen_chat`, This is similar to the parameter :paramref:`switch_inline_query_chosen_chat`,
but gives no control over which chats can be selected. but gives no control over which chats can be selected.
switch_inline_query_current_chat (:obj:`str`, optional): If set, pressing the button will 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 insert the bot's username and the specified inline query in the current chat's input
@ -137,7 +135,7 @@ class InlineKeyboardButton(TelegramObject):
Note: Note:
This type of button **must** always be the first button in the first row and can This type of button **must** always be the first button in the first row and can
only be used in invoice messages. only be used in invoice messages.
switch_inline_query_chosen_chat (:obj:`telegram.SwitchInlineQueryChosenChat`, optional): switch_inline_query_chosen_chat (:class:`telegram.SwitchInlineQueryChosenChat`, optional):
If set, pressing the button will prompt the user to select one of their chats of the 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 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 query in the input field. Not supported for messages sent on behalf of a Telegram
@ -170,7 +168,7 @@ class InlineKeyboardButton(TelegramObject):
to the bot when the button is pressed, UTF-8 to the bot when the button is pressed, UTF-8
:tg-const:`telegram.InlineKeyboardButton.MIN_CALLBACK_DATA`- :tg-const:`telegram.InlineKeyboardButton.MIN_CALLBACK_DATA`-
:tg-const:`telegram.InlineKeyboardButton.MAX_CALLBACK_DATA` bytes. :tg-const:`telegram.InlineKeyboardButton.MAX_CALLBACK_DATA` bytes.
web_app (:obj:`telegram.WebAppInfo`): Optional. Description of the `Web App web_app (:class:`telegram.WebAppInfo`): Optional. Description of the `Web App
<https://core.telegram.org/bots/webapps>`_ that will be launched when the user presses <https://core.telegram.org/bots/webapps>`_ that will be launched when the user presses
the button. The Web App will be able to send an arbitrary message on behalf of the user 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 using the method :meth:`~telegram.Bot.answer_web_app_query`. Available only in
@ -178,16 +176,14 @@ class InlineKeyboardButton(TelegramObject):
a Telegram Business account. a Telegram Business account.
.. versionadded:: 20.0 .. versionadded:: 20.0
switch_inline_query (:obj:`str`): Optional. If set, pressing the button will insert the switch_inline_query (:obj:`str`): Optional. If set, pressing the button will prompt the
bot's username and the specified inline query in the current chat's input field. May be user to select one of their chats, open that chat and insert the bot's username and the
empty, in which case only the bot's username will be inserted. 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
This offers a quick way for the user to open your bot in inline mode in the same chat - Business account.
good for selecting something from multiple options. Not supported in channels and for
messages sent on behalf of a Telegram Business account.
Tip: Tip:
This is similar to the new parameter :paramref:`switch_inline_query_chosen_chat`, This is similar to the parameter :paramref:`switch_inline_query_chosen_chat`,
but gives no control over which chats can be selected. but gives no control over which chats can be selected.
switch_inline_query_current_chat (:obj:`str`): Optional. If set, pressing the button will 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 insert the bot's username and the specified inline query in the current chat's input
@ -208,7 +204,7 @@ class InlineKeyboardButton(TelegramObject):
Note: Note:
This type of button **must** always be the first button in the first row and can This type of button **must** always be the first button in the first row and can
only be used in invoice messages. only be used in invoice messages.
switch_inline_query_chosen_chat (:obj:`telegram.SwitchInlineQueryChosenChat`): Optional. switch_inline_query_chosen_chat (:class:`telegram.SwitchInlineQueryChosenChat`): Optional.
If set, pressing the button will prompt the user to select one of their chats of the 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 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 query in the input field. Not supported for messages sent on behalf of a Telegram

View file

@ -50,16 +50,14 @@ class InlineQueryResultGif(InlineQueryResult):
gif_width (:obj:`int`, optional): Width of the GIF. gif_width (:obj:`int`, optional): Width of the GIF.
gif_height (:obj:`int`, optional): Height of the GIF. gif_height (:obj:`int`, optional): Height of the GIF.
gif_duration (:obj:`int`, optional): Duration of the GIF in seconds. gif_duration (:obj:`int`, optional): Duration of the GIF in seconds.
thumbnail_url (:obj:`str`, optional): URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4)
thumbnail for the result. thumbnail for the result.
Warning:
The Bot API does **not** define this as an optional argument. It is formally
optional for backwards compatibility with the deprecated :paramref:`thumb_url`.
If you pass neither :paramref:`thumbnail_url` nor :paramref:`thumb_url`,
:class:`ValueError` will be raised.
.. versionadded:: 20.2 .. versionadded:: 20.2
..versionchanged:: 20.5
|thumbnail_url_mandatory|
thumbnail_mime_type (:obj:`str`, optional): MIME type of the thumbnail, must be one of 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'``. ``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``.
@ -82,10 +80,6 @@ class InlineQueryResultGif(InlineQueryResult):
.. versionadded:: 21.3 .. versionadded:: 21.3
Raises:
:class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is
supplied or if both are supplied and are not equal.
Attributes: Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.GIF`. type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.GIF`.
id (:obj:`str`): Unique identifier for this result, id (:obj:`str`): Unique identifier for this result,

View file

@ -51,16 +51,14 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
mpeg4_width (:obj:`int`, optional): Video width. mpeg4_width (:obj:`int`, optional): Video width.
mpeg4_height (:obj:`int`, optional): Video height. mpeg4_height (:obj:`int`, optional): Video height.
mpeg4_duration (:obj:`int`, optional): Video duration in seconds. mpeg4_duration (:obj:`int`, optional): Video duration in seconds.
thumbnail_url (:obj:`str`, optional): URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4)
thumbnail for the result. thumbnail for the result.
Warning:
The Bot API does **not** define this as an optional argument. It is formally
optional for backwards compatibility with the deprecated :paramref:`thumb_url`.
If you pass neither :paramref:`thumbnail_url` nor :paramref:`thumb_url`,
:class:`ValueError` will be raised.
.. versionadded:: 20.2 .. versionadded:: 20.2
..versionchanged:: 20.5
|thumbnail_url_mandatory|
thumbnail_mime_type (:obj:`str`, optional): MIME type of the thumbnail, must be one of 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'``. ``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``.
@ -83,9 +81,6 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med|
.. versionadded:: 21.3 .. versionadded:: 21.3
Raises:
:class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is
supplied or if both are supplied and are not equal.
Attributes: Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.MPEG4GIF`. type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.MPEG4GIF`.

View file

@ -48,15 +48,13 @@ class InlineQueryResultPhoto(InlineQueryResult):
:tg-const:`telegram.InlineQueryResult.MAX_ID_LENGTH` Bytes. :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 photo_url (:obj:`str`): A valid URL of the photo. Photo must be in JPEG format. Photo size
must not exceed 5MB. must not exceed 5MB.
thumbnail_url (:obj:`str`, optional): URL of the thumbnail for the photo. thumbnail_url (:obj:`str`): URL of the thumbnail for the photo.
Warning:
The Bot API does **not** define this as an optional argument. It is formally
optional for backwards compatibility with the deprecated :paramref:`thumb_url`.
If you pass neither :paramref:`thumbnail_url` nor :paramref:`thumb_url`,
:class:`ValueError` will be raised.
.. versionadded:: 20.2 .. versionadded:: 20.2
..versionchanged:: 20.5
|thumbnail_url_mandatory|
photo_width (:obj:`int`, optional): Width of the photo. photo_width (:obj:`int`, optional): Width of the photo.
photo_height (:obj:`int`, optional): Height of the photo. photo_height (:obj:`int`, optional): Height of the photo.
title (:obj:`str`, optional): Title for the result. title (:obj:`str`, optional): Title for the result.
@ -78,10 +76,6 @@ class InlineQueryResultPhoto(InlineQueryResult):
.. versionadded:: 21.3 .. versionadded:: 21.3
Raises:
:class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is
supplied or if both are supplied and are not equal.
Attributes: Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.PHOTO`. type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.PHOTO`.
id (:obj:`str`): Unique identifier for this result, id (:obj:`str`): Unique identifier for this result,

View file

@ -55,20 +55,12 @@ class InlineQueryResultVideo(InlineQueryResult):
mime_type (:obj:`str`): Mime type of the content of video url, "text/html" or "video/mp4". 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. thumbnail_url (:obj:`str`, optional): URL of the thumbnail (JPEG only) for the video.
Warning:
The Bot API does **not** define this as an optional argument. It is formally
optional for backwards compatibility with the deprecated :paramref:`thumb_url`.
If you pass neither :paramref:`thumbnail_url` nor :paramref:`thumb_url`,
:class:`ValueError` will be raised.
.. versionadded:: 20.2 .. versionadded:: 20.2
title (:obj:`str`, optional): Title for the result.
Warning: ..versionchanged:: 20.5
The Bot API does **not** define this as an optional argument. It is formally |thumbnail_url_mandatory|
optional to ensure backwards compatibility of :paramref:`thumbnail_url` with the
deprecated :paramref:`thumb_url`, which required that :paramref:`thumbnail_url` title (:obj:`str`): Title for the result.
become optional. :class:`TypeError` will be raised if no ``title`` is passed.
caption (:obj:`str`, optional): Caption of the video to be sent, caption (:obj:`str`, optional): Caption of the video to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities
parsing. parsing.
@ -92,11 +84,6 @@ class InlineQueryResultVideo(InlineQueryResult):
.. versionadded:: 21.3 .. versionadded:: 21.3
Raises:
:class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is
supplied or if both are supplied and are not equal.
:class:`TypeError`: If no :paramref:`title` is passed.
Attributes: Attributes:
type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VIDEO`. type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VIDEO`.
id (:obj:`str`): Unique identifier for this result, id (:obj:`str`): Unique identifier for this result,

View file

@ -47,7 +47,7 @@ class InputInvoiceMessageContent(InputMessageContent):
payload (:obj:`str`): Bot-defined invoice payload. payload (:obj:`str`): Bot-defined invoice payload.
:tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`- :tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`-
:tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be displayed :tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be displayed
to the user, use for your internal processes. to the user, use it for your internal processes.
provider_token (:obj:`str`): Payment provider token, obtained via provider_token (:obj:`str`): Payment provider token, obtained via
`@Botfather <https://t.me/Botfather>`_. Pass an empty string for payments in `@Botfather <https://t.me/Botfather>`_. Pass an empty string for payments in
|tg_stars|. |tg_stars|.
@ -115,7 +115,7 @@ class InputInvoiceMessageContent(InputMessageContent):
payload (:obj:`str`): Bot-defined invoice payload. payload (:obj:`str`): Bot-defined invoice payload.
:tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`- :tg-const:`telegram.Invoice.MIN_PAYLOAD_LENGTH`-
:tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be displayed :tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be displayed
to the user, use for your internal processes. to the user, use it for your internal processes.
provider_token (:obj:`str`): Payment provider token, obtained via provider_token (:obj:`str`): Payment provider token, obtained via
`@Botfather <https://t.me/Botfather>`_. Pass an empty string for payments in `Telegram `@Botfather <https://t.me/Botfather>`_. Pass an empty string for payments in `Telegram
Stars <https://t.me/BotNews/90>`_. Stars <https://t.me/BotNews/90>`_.

View file

@ -280,15 +280,14 @@ class Message(MaybeInaccessibleMessage):
Args: Args:
message_id (:obj:`int`): Unique message identifier inside this chat. message_id (:obj:`int`): Unique message identifier inside this chat.
from_user (:class:`telegram.User`, optional): Sender of the message; empty for messages from_user (:class:`telegram.User`, optional): Sender of the message; may be empty for
sent to channels. For backward compatibility, this will contain a fake sender user in messages sent to channels. For backward compatibility, if the message was sent on
non-channel chats, if the message was sent on behalf of a chat. 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, sent on behalf of a sender_chat (:class:`telegram.Chat`, optional): Sender of the message when sent on behalf
chat. For example, the channel itself for channel posts, the supergroup itself for of a chat. For example, the supergroup itself for messages sent by its anonymous
messages from anonymous group administrators, the linked channel for messages administrators or a linked channel for messages automatically forwarded to the
automatically forwarded to the discussion group. For backward compatibility, channel's discussion group. For backward compatibility, if the message was sent on
:attr:`from_user` contains a fake sender user in non-channel chats, if the message was behalf of a chat, the field from contains a fake sender user in non-channel chats.
sent on behalf of a chat.
date (:class:`datetime.datetime`): Date the message was sent in Unix time. Converted to date (:class:`datetime.datetime`): Date the message was sent in Unix time. Converted to
:class:`datetime.datetime`. :class:`datetime.datetime`.
@ -570,36 +569,35 @@ class Message(MaybeInaccessibleMessage):
.. versionadded:: 21.1 .. versionadded:: 21.1
sender_business_bot (:obj:`telegram.User`, optional): The bot that actually sent the sender_business_bot (:class:`telegram.User`, optional): The bot that actually sent the
message on behalf of the business account. Available only for outgoing messages sent message on behalf of the business account. Available only for outgoing messages sent
on behalf of the connected business account. on behalf of the connected business account.
.. versionadded:: 21.1 .. versionadded:: 21.1
chat_background_set (:obj:`telegram.ChatBackground`, optional): Service message: chat chat_background_set (:class:`telegram.ChatBackground`, optional): Service message: chat
background set. background set.
.. versionadded:: 21.2 .. versionadded:: 21.2
paid_media (:obj:`telegram.PaidMediaInfo`, optional): Message contains paid media; paid_media (:class:`telegram.PaidMediaInfo`, optional): Message contains paid media;
information about the paid media. information about the paid media.
.. versionadded:: 21.4 .. versionadded:: 21.4
refunded_payment (:obj:`telegram.RefundedPayment`, optional): Message is a service message refunded_payment (:class:`telegram.RefundedPayment`, optional): Message is a service
about a refunded payment, information about the payment. message about a refunded payment, information about the payment.
.. versionadded:: 21.4 .. versionadded:: 21.4
Attributes: Attributes:
message_id (:obj:`int`): Unique message identifier inside this chat. message_id (:obj:`int`): Unique message identifier inside this chat.
from_user (:class:`telegram.User`): Optional. Sender of the message; empty for messages from_user (:class:`telegram.User`): Optional. Sender of the message; may be empty for
sent to channels. For backward compatibility, this will contain a fake sender user in messages sent to channels. For backward compatibility, if the message was sent on
non-channel chats, if the message was sent on behalf of a chat. 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, sent on behalf of a sender_chat (:class:`telegram.Chat`): Optional. Sender of the message when sent on behalf
chat. For example, the channel itself for channel posts, the supergroup itself for of a chat. For example, the supergroup itself for messages sent by its anonymous
messages from anonymous group administrators, the linked channel for messages administrators or a linked channel for messages automatically forwarded to the
automatically forwarded to the discussion group. For backward compatibility, channel's discussion group. For backward compatibility, if the message was sent on
:attr:`from_user` contains a fake sender user in non-channel chats, if the message was behalf of a chat, the field from contains a fake sender user in non-channel chats.
sent on behalf of a chat.
date (:class:`datetime.datetime`): Date the message was sent in Unix time. Converted to date (:class:`datetime.datetime`): Date the message was sent in Unix time. Converted to
:class:`datetime.datetime`. :class:`datetime.datetime`.
@ -897,22 +895,22 @@ class Message(MaybeInaccessibleMessage):
.. versionadded:: 21.1 .. versionadded:: 21.1
sender_business_bot (:obj:`telegram.User`): Optional. The bot that actually sent the sender_business_bot (:class:`telegram.User`): Optional. The bot that actually sent the
message on behalf of the business account. Available only for outgoing messages sent message on behalf of the business account. Available only for outgoing messages sent
on behalf of the connected business account. on behalf of the connected business account.
.. versionadded:: 21.1 .. versionadded:: 21.1
chat_background_set (:obj:`telegram.ChatBackground`): Optional. Service message: chat chat_background_set (:class:`telegram.ChatBackground`): Optional. Service message: chat
background set background set
.. versionadded:: 21.2 .. versionadded:: 21.2
paid_media (:obj:`telegram.PaidMediaInfo`): Optional. Message contains paid media; paid_media (:class:`telegram.PaidMediaInfo`): Optional. Message contains paid media;
information about the paid media. information about the paid media.
.. versionadded:: 21.4 .. versionadded:: 21.4
refunded_payment (:obj:`telegram.RefundedPayment`): Optional. Message is a service message refunded_payment (:class:`telegram.RefundedPayment`): Optional. Message is a service
about a refunded payment, information about the payment. message about a refunded payment, information about the payment.
.. versionadded:: 21.4 .. versionadded:: 21.4
@ -4114,7 +4112,7 @@ class Message(MaybeInaccessibleMessage):
For the documentation of the arguments, please see :meth:`telegram.Bot.pin_chat_message`. For the documentation of the arguments, please see :meth:`telegram.Bot.pin_chat_message`.
.. versionchanged:: NEXT.VERSION .. versionchanged:: 21.5
Now also passes :attr:`business_connection_id` to Now also passes :attr:`business_connection_id` to
:meth:`telegram.Bot.pin_chat_message`. :meth:`telegram.Bot.pin_chat_message`.
@ -4154,7 +4152,7 @@ class Message(MaybeInaccessibleMessage):
For the documentation of the arguments, please see :meth:`telegram.Bot.unpin_chat_message`. For the documentation of the arguments, please see :meth:`telegram.Bot.unpin_chat_message`.
.. versionchanged:: NEXT.VERSION .. versionchanged:: 21.5
Now also passes :attr:`business_connection_id` to Now also passes :attr:`business_connection_id` to
:meth:`telegram.Bot.pin_chat_message`. :meth:`telegram.Bot.pin_chat_message`.

View file

@ -20,7 +20,7 @@
import copy import copy
import itertools import itertools
from typing import TYPE_CHECKING, Dict, Final, List, Optional, Sequence from typing import TYPE_CHECKING, Dict, Final, List, Optional, Sequence, Tuple, Union
from telegram import constants from telegram import constants
from telegram._telegramobject import TelegramObject from telegram._telegramobject import TelegramObject
@ -32,6 +32,8 @@ from telegram._utils.types import JSONDict
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Bot from telegram import Bot
_SEM = Sequence["MessageEntity"]
class MessageEntity(TelegramObject): class MessageEntity(TelegramObject):
""" """
@ -146,9 +148,7 @@ class MessageEntity(TelegramObject):
return super().de_json(data=data, bot=bot) return super().de_json(data=data, bot=bot)
@staticmethod @staticmethod
def adjust_message_entities_to_utf_16( def adjust_message_entities_to_utf_16(text: str, entities: _SEM) -> _SEM:
text: str, entities: Sequence["MessageEntity"]
) -> Sequence["MessageEntity"]:
"""Utility functionality for converting the offset and length of entities from """Utility functionality for converting the offset and length of entities from
Unicode (:obj:`str`) to UTF-16 (``utf-16-le`` encoded :obj:`bytes`). Unicode (:obj:`str`) to UTF-16 (``utf-16-le`` encoded :obj:`bytes`).
@ -206,7 +206,7 @@ class MessageEntity(TelegramObject):
text_slice = text[last_position:position] text_slice = text[last_position:position]
accumulated_length += len(text_slice.encode(TextEncoding.UTF_16_LE)) // 2 accumulated_length += len(text_slice.encode(TextEncoding.UTF_16_LE)) // 2
position_translation[position] = accumulated_length position_translation[position] = accumulated_length
# get the final output entites # get the final output entities
out = [] out = []
for entity in entities: for entity in entities:
translated_positions = position_translation[entity.offset] translated_positions = position_translation[entity.offset]
@ -220,6 +220,143 @@ class MessageEntity(TelegramObject):
out.append(new_entity) out.append(new_entity)
return out 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) ALL_TYPES: Final[List[str]] = list(constants.MessageEntityType)
"""List[:obj:`str`]: A list of all available message entity types.""" """List[:obj:`str`]: A list of all available message entity types."""
BLOCKQUOTE: Final[str] = constants.MessageEntityType.BLOCKQUOTE BLOCKQUOTE: Final[str] = constants.MessageEntityType.BLOCKQUOTE

View file

@ -112,7 +112,7 @@ class EncryptedCredentials(TelegramObject):
Note: Note:
This object is decrypted only when originating from This object is decrypted only when originating from
:obj:`telegram.PassportData.decrypted_credentials`. :attr:`telegram.PassportData.decrypted_credentials`.
Args: Args:
data (:class:`telegram.Credentials` | :obj:`str`): Decrypted data with unique user's data (:class:`telegram.Credentials` | :obj:`str`): Decrypted data with unique user's

View file

@ -42,7 +42,7 @@ class EncryptedPassportElement(TelegramObject):
Note: Note:
This object is decrypted only when originating from This object is decrypted only when originating from
:obj:`telegram.PassportData.decrypted_data`. :attr:`telegram.PassportData.decrypted_data`.
Args: Args:
type (:obj:`str`): Element type. One of "personal_details", "passport", "driver_license", type (:obj:`str`): Element type. One of "personal_details", "passport", "driver_license",

View file

@ -203,7 +203,7 @@ class PassportFile(TelegramObject):
""" """
Wrapper over :meth:`telegram.Bot.get_file`. Will automatically assign the correct Wrapper over :meth:`telegram.Bot.get_file`. Will automatically assign the correct
credentials to the returned :class:`telegram.File` if originating from credentials to the returned :class:`telegram.File` if originating from
:obj:`telegram.PassportData.decrypted_data`. :attr:`telegram.PassportData.decrypted_data`.
For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`.

View file

@ -23,6 +23,7 @@ from datetime import datetime
from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type
from telegram import constants from telegram import constants
from telegram._paidmedia import PaidMedia
from telegram._telegramobject import TelegramObject from telegram._telegramobject import TelegramObject
from telegram._user import User from telegram._user import User
from telegram._utils import enum from telegram._utils import enum
@ -74,6 +75,17 @@ class RevenueWithdrawalState(TelegramObject):
def de_json( def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["RevenueWithdrawalState"]: ) -> Optional["RevenueWithdrawalState"]:
"""Converts JSON data to the appropriate :class:`RevenueWithdrawalState` object, i.e. takes
care of selecting the correct subclass.
Args:
data (Dict[:obj:`str`, ...]): The JSON data.
bot (:class:`telegram.Bot`): The bot associated with this object.
Returns:
The Telegram object.
"""
data = cls._parse_data(data) data = cls._parse_data(data)
if not data: if not data:
@ -150,6 +162,7 @@ class RevenueWithdrawalStateSucceeded(RevenueWithdrawalState):
def de_json( def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["RevenueWithdrawalStateSucceeded"]: ) -> Optional["RevenueWithdrawalStateSucceeded"]:
"""See :meth:`telegram.RevenueWithdrawalState.de_json`."""
data = cls._parse_data(data) data = cls._parse_data(data)
if not data: if not data:
@ -260,13 +273,13 @@ class TransactionPartnerFragment(TransactionPartner):
.. versionadded:: 21.4 .. versionadded:: 21.4
Args: Args:
withdrawal_state (:obj:`telegram.RevenueWithdrawalState`, optional): State of the withdrawal_state (:class:`telegram.RevenueWithdrawalState`, optional): State of the
transaction if the transaction is outgoing. transaction if the transaction is outgoing.
Attributes: Attributes:
type (:obj:`str`): The type of the transaction partner, type (:obj:`str`): The type of the transaction partner,
always :tg-const:`telegram.TransactionPartner.FRAGMENT`. always :tg-const:`telegram.TransactionPartner.FRAGMENT`.
withdrawal_state (:obj:`telegram.RevenueWithdrawalState`): Optional. State of the withdrawal_state (:class:`telegram.RevenueWithdrawalState`): Optional. State of the
transaction if the transaction is outgoing. transaction if the transaction is outgoing.
""" """
@ -287,6 +300,7 @@ class TransactionPartnerFragment(TransactionPartner):
def de_json( def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["TransactionPartnerFragment"]: ) -> Optional["TransactionPartnerFragment"]:
"""See :meth:`telegram.TransactionPartner.de_json`."""
data = cls._parse_data(data) data = cls._parse_data(data)
if not data: if not data:
@ -310,20 +324,33 @@ class TransactionPartnerUser(TransactionPartner):
Args: Args:
user (:class:`telegram.User`): Information about the user. user (:class:`telegram.User`): Information about the user.
invoice_payload (:obj:`str`, optional): Bot-specified invoice payload. invoice_payload (:obj:`str`, optional): Bot-specified invoice payload.
paid_media (Sequence[:class:`telegram.PaidMedia`], optional): Information about the paid
media bought by the user.
.. versionadded:: 21.5
Attributes: Attributes:
type (:obj:`str`): The type of the transaction partner, type (:obj:`str`): The type of the transaction partner,
always :tg-const:`telegram.TransactionPartner.USER`. always :tg-const:`telegram.TransactionPartner.USER`.
user (:class:`telegram.User`): Information about the user. user (:class:`telegram.User`): Information about the user.
invoice_payload (:obj:`str`): Optional. Bot-specified invoice payload. invoice_payload (:obj:`str`): Optional. Bot-specified invoice payload.
paid_media (Tuple[:class:`telegram.PaidMedia`]): Optional. Information about the paid
media bought by the user.
.. versionadded:: 21.5
""" """
__slots__ = ("invoice_payload", "user") __slots__ = (
"invoice_payload",
"paid_media",
"user",
)
def __init__( def __init__(
self, self,
user: "User", user: "User",
invoice_payload: Optional[str] = None, invoice_payload: Optional[str] = None,
paid_media: Optional[Sequence[PaidMedia]] = None,
*, *,
api_kwargs: Optional[JSONDict] = None, api_kwargs: Optional[JSONDict] = None,
) -> None: ) -> None:
@ -332,6 +359,7 @@ class TransactionPartnerUser(TransactionPartner):
with self._unfrozen(): with self._unfrozen():
self.user: User = user self.user: User = user
self.invoice_payload: Optional[str] = invoice_payload self.invoice_payload: Optional[str] = invoice_payload
self.paid_media: Optional[Tuple[PaidMedia, ...]] = parse_sequence_arg(paid_media)
self._id_attrs = ( self._id_attrs = (
self.type, self.type,
self.user, self.user,
@ -341,12 +369,14 @@ class TransactionPartnerUser(TransactionPartner):
def de_json( def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["TransactionPartnerUser"]: ) -> Optional["TransactionPartnerUser"]:
"""See :meth:`telegram.TransactionPartner.de_json`."""
data = cls._parse_data(data) data = cls._parse_data(data)
if not data: if not data:
return None return None
data["user"] = User.de_json(data.get("user"), bot) data["user"] = User.de_json(data.get("user"), bot)
data["paid_media"] = PaidMedia.de_list(data.get("paid_media"), bot=bot)
return super().de_json(data=data, bot=bot) # type: ignore[return-value] return super().de_json(data=data, bot=bot) # type: ignore[return-value]
@ -452,6 +482,7 @@ class StarTransaction(TelegramObject):
def de_json( def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["StarTransaction"]: ) -> Optional["StarTransaction"]:
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data) data = cls._parse_data(data)
if not data: if not data:
@ -498,6 +529,7 @@ class StarTransactions(TelegramObject):
def de_json( def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["StarTransactions"]: ) -> Optional["StarTransactions"]:
"""See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data) data = cls._parse_data(data)
if data is None: if data is None:

View file

@ -17,7 +17,8 @@
# You should have received a copy of the GNU Lesser Public License # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains objects that represents a Telegram ReactionType.""" """This module contains objects that represents a Telegram ReactionType."""
from typing import TYPE_CHECKING, Final, Literal, Optional, Union
from typing import TYPE_CHECKING, Dict, Final, Literal, Optional, Type, Union
from telegram import constants from telegram import constants
from telegram._telegramobject import TelegramObject from telegram._telegramobject import TelegramObject
@ -30,16 +31,22 @@ if TYPE_CHECKING:
class ReactionType(TelegramObject): class ReactionType(TelegramObject):
"""Base class for Telegram ReactionType Objects. """Base class for Telegram ReactionType Objects.
There exist :class:`telegram.ReactionTypeEmoji` and :class:`telegram.ReactionTypeCustomEmoji`. There exist :class:`telegram.ReactionTypeEmoji`, :class:`telegram.ReactionTypeCustomEmoji`
and :class:`telegram.ReactionTypePaid`.
.. versionadded:: 20.8 .. versionadded:: 20.8
.. versionchanged:: 21.5
Added paid reaction.
Args: Args:
type (:obj:`str`): Type of the reaction. Can be type (:obj:`str`): Type of the reaction. Can be
:attr:`~telegram.ReactionType.EMOJI` or :attr:`~telegram.ReactionType.CUSTOM_EMOJI`. :attr:`~telegram.ReactionType.EMOJI`, :attr:`~telegram.ReactionType.CUSTOM_EMOJI` or
:attr:`~telegram.ReactionType.PAID`.
Attributes: Attributes:
type (:obj:`str`): Type of the reaction. Can be type (:obj:`str`): Type of the reaction. Can be
:attr:`~telegram.ReactionType.EMOJI` or :attr:`~telegram.ReactionType.CUSTOM_EMOJI`. :attr:`~telegram.ReactionType.EMOJI`, :attr:`~telegram.ReactionType.CUSTOM_EMOJI` or
:attr:`~telegram.ReactionType.PAID`.
""" """
@ -49,11 +56,16 @@ class ReactionType(TelegramObject):
""":const:`telegram.constants.ReactionType.EMOJI`""" """:const:`telegram.constants.ReactionType.EMOJI`"""
CUSTOM_EMOJI: Final[constants.ReactionType] = constants.ReactionType.CUSTOM_EMOJI CUSTOM_EMOJI: Final[constants.ReactionType] = constants.ReactionType.CUSTOM_EMOJI
""":const:`telegram.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__( def __init__(
self, self,
type: Union[ # pylint: disable=redefined-builtin type: Union[ # pylint: disable=redefined-builtin
Literal["emoji", "custom_emoji"], constants.ReactionType Literal["emoji", "custom_emoji", "paid"], constants.ReactionType
], ],
*, *,
api_kwargs: Optional[JSONDict] = None, api_kwargs: Optional[JSONDict] = None,
@ -71,14 +83,20 @@ class ReactionType(TelegramObject):
"""See :meth:`telegram.TelegramObject.de_json`.""" """See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data) data = cls._parse_data(data)
if not data: if data is None:
return None return None
if cls is ReactionType and data.get("type") in [cls.EMOJI, cls.CUSTOM_EMOJI]: if not data and cls is ReactionType:
reaction_type = data.pop("type") return None
if reaction_type == cls.EMOJI:
return ReactionTypeEmoji.de_json(data=data, bot=bot) _class_mapping: Dict[str, Type[ReactionType]] = {
return ReactionTypeCustomEmoji.de_json(data=data, bot=bot) 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) return super().de_json(data=data, bot=bot)
@ -152,6 +170,24 @@ class ReactionTypeCustomEmoji(ReactionType):
self._id_attrs = (self.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): class ReactionCount(TelegramObject):
"""This class represents a reaction added to a message along with the number of times it was """This class represents a reaction added to a message along with the number of times it was
added. added.

View file

@ -250,7 +250,7 @@ class ExternalReplyInfo(TelegramObject):
def de_json( def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["ExternalReplyInfo"]: ) -> Optional["ExternalReplyInfo"]:
"""See :obj:`telegram.TelegramObject.de_json`.""" """See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data) data = cls._parse_data(data)
if data is None: if data is None:
@ -299,7 +299,8 @@ class TextQuote(TelegramObject):
message. message.
position (:obj:`int`): Approximate quote position in the original message in UTF-16 code position (:obj:`int`): Approximate quote position in the original message in UTF-16 code
units as specified by the sender. units as specified by the sender.
entities (Sequence[:obj:`telegram.MessageEntity`], optional): Special entities that appear entities (Sequence[:class:`telegram.MessageEntity`], optional): Special entities that
appear
in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and
custom_emoji entities are kept in quotes. custom_emoji entities are kept in quotes.
is_manual (:obj:`bool`, optional): :obj:`True`, if the quote was chosen manually by the is_manual (:obj:`bool`, optional): :obj:`True`, if the quote was chosen manually by the
@ -310,7 +311,7 @@ class TextQuote(TelegramObject):
message. message.
position (:obj:`int`): Approximate quote position in the original message in UTF-16 code position (:obj:`int`): Approximate quote position in the original message in UTF-16 code
units as specified by the sender. units as specified by the sender.
entities (Tuple[:obj:`telegram.MessageEntity`]): Optional. Special entities that appear entities (Tuple[:class:`telegram.MessageEntity`]): Optional. Special entities that appear
in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and
custom_emoji entities are kept in quotes. custom_emoji entities are kept in quotes.
is_manual (:obj:`bool`): Optional. :obj:`True`, if the quote was chosen manually by the is_manual (:obj:`bool`): Optional. :obj:`True`, if the quote was chosen manually by the
@ -351,7 +352,7 @@ class TextQuote(TelegramObject):
def de_json( def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["TextQuote"]: ) -> Optional["TextQuote"]:
"""See :obj:`telegram.TelegramObject.de_json`.""" """See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data) data = cls._parse_data(data)
if data is None: if data is None:
@ -387,7 +388,8 @@ class ReplyParameters(TelegramObject):
quote_parse_mode (:obj:`str`, optional): Mode for parsing entities in the quote. See quote_parse_mode (:obj:`str`, optional): Mode for parsing entities in the quote. See
:wiki:`formatting options <Code-snippets#message-formatting-bold-italic-code->` for :wiki:`formatting options <Code-snippets#message-formatting-bold-italic-code->` for
more details. more details.
quote_entities (Sequence[:obj:`telegram.MessageEntity`], optional): A JSON-serialized list quote_entities (Sequence[:class:`telegram.MessageEntity`], optional): A JSON-serialized
list
of special entities that appear in the quote. It can be specified instead of of special entities that appear in the quote. It can be specified instead of
:paramref:`quote_parse_mode`. :paramref:`quote_parse_mode`.
quote_position (:obj:`int`, optional): Position of the quote in the original message in quote_position (:obj:`int`, optional): Position of the quote in the original message in
@ -409,8 +411,8 @@ class ReplyParameters(TelegramObject):
quote_parse_mode (:obj:`str`): Optional. Mode for parsing entities in the quote. See quote_parse_mode (:obj:`str`): Optional. Mode for parsing entities in the quote. See
:wiki:`formatting options <Code-snippets#message-formatting-bold-italic-code->` for :wiki:`formatting options <Code-snippets#message-formatting-bold-italic-code->` for
more details. more details.
quote_entities (Tuple[:obj:`telegram.MessageEntity`]): Optional. A JSON-serialized list of quote_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. A JSON-serialized list
special entities that appear in the quote. It can be specified instead of of special entities that appear in the quote. It can be specified instead of
:paramref:`quote_parse_mode`. :paramref:`quote_parse_mode`.
quote_position (:obj:`int`): Optional. Position of the quote in the original message in quote_position (:obj:`int`): Optional. Position of the quote in the original message in
UTF-16 code units. UTF-16 code units.
@ -458,7 +460,7 @@ class ReplyParameters(TelegramObject):
def de_json( def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["ReplyParameters"]: ) -> Optional["ReplyParameters"]:
"""See :obj:`telegram.TelegramObject.de_json`.""" """See :meth:`telegram.TelegramObject.de_json`."""
data = cls._parse_data(data) data = cls._parse_data(data)
if data is None: if data is None:

View file

@ -354,7 +354,7 @@ class TelegramObject:
memodict (:obj:`dict`): A dictionary that maps objects to their copies. memodict (:obj:`dict`): A dictionary that maps objects to their copies.
Returns: Returns:
:obj:`telegram.TelegramObject`: The copied object. :class:`telegram.TelegramObject`: The copied object.
""" """
bot = self._bot # Save bot so we can set it after copying bot = self._bot # Save bot so we can set it after copying
self.set_bot(None) # set to None so it is not deepcopied self.set_bot(None) # set to None so it is not deepcopied

View file

@ -100,7 +100,7 @@ class User(TelegramObject):
has_main_web_app (:obj:`bool`, optional): :obj:`True`, if the bot has the main Web App. 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`. Returned only in :meth:`telegram.Bot.get_me`.
.. versionadded:: NEXT.VERSION .. versionadded:: 21.5
Attributes: Attributes:
id (:obj:`int`): Unique identifier for this user or bot. id (:obj:`int`): Unique identifier for this user or bot.
@ -131,7 +131,7 @@ class User(TelegramObject):
has_main_web_app (:obj:`bool`) Optional. :obj:`True`, if the bot has the main Web App. 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`. Returned only in :meth:`telegram.Bot.get_me`.
.. versionadded:: NEXT.VERSION .. versionadded:: 21.5
.. |user_chat_id_note| replace:: This shortcuts build on the assumption that :attr:`User.id` .. |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 coincides with the :attr:`Chat.id` of the private chat with the user. This has been the

View file

@ -33,7 +33,7 @@ from telegram._utils.enum import StringEnum
class TextEncoding(StringEnum): class TextEncoding(StringEnum):
"""This enum contains encoding schemes for text. """This enum contains encoding schemes for text.
.. versionadded:: NEXT.VERSION .. versionadded:: 21.5
""" """
__slots__ = () __slots__ = ()

View file

@ -51,6 +51,6 @@ class Version(NamedTuple):
__version_info__: Final[Version] = Version( __version_info__: Final[Version] = Version(
major=21, minor=4, micro=0, releaselevel="final", serial=0 major=21, minor=5, micro=0, releaselevel="final", serial=0
) )
__version__: Final[str] = str(__version_info__) __version__: Final[str] = str(__version_info__)

View file

@ -54,6 +54,7 @@ __all__ = [
"ChatLimit", "ChatLimit",
"ChatMemberStatus", "ChatMemberStatus",
"ChatPhotoSize", "ChatPhotoSize",
"ChatSubscriptionLimit",
"ChatType", "ChatType",
"ContactLimit", "ContactLimit",
"CustomEmojiStickerLimit", "CustomEmojiStickerLimit",
@ -151,7 +152,7 @@ class _AccentColor(NamedTuple):
#: :data:`telegram.__bot_api_version_info__`. #: :data:`telegram.__bot_api_version_info__`.
#: #:
#: .. versionadded:: 20.0 #: .. versionadded:: 20.0
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=8) BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=9)
#: :obj:`str`: Telegram Bot API #: :obj:`str`: Telegram Bot API
#: version supported by this version of `python-telegram-bot`. Also available as #: version supported by this version of `python-telegram-bot`. Also available as
#: :data:`telegram.__bot_api_version__`. #: :data:`telegram.__bot_api_version__`.
@ -2903,6 +2904,11 @@ class ReactionType(StringEnum):
""":obj:`str`: A :class:`telegram.ReactionType` with a normal emoji.""" """:obj:`str`: A :class:`telegram.ReactionType` with a normal emoji."""
CUSTOM_EMOJI = "custom_emoji" CUSTOM_EMOJI = "custom_emoji"
""":obj:`str`: A :class:`telegram.ReactionType` with a 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): class ReactionEmoji(StringEnum):
@ -3096,3 +3102,22 @@ class BackgroundFillType(StringEnum):
""":obj:`str`: A :class:`telegram.BackgroundFill` with gradient fill.""" """:obj:`str`: A :class:`telegram.BackgroundFill` with gradient fill."""
FREEFORM_GRADIENT = "freeform_gradient" FREEFORM_GRADIENT = "freeform_gradient"
""":obj:`str`: A :class:`telegram.BackgroundFill` with freeform_gradient fill.""" """: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."""

View file

@ -4234,6 +4234,7 @@ class ExtBot(Bot, Generic[RLARGS]):
protect_content: ODVInput[bool] = DEFAULT_NONE, protect_content: ODVInput[bool] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None, reply_parameters: Optional["ReplyParameters"] = None,
reply_markup: Optional[ReplyMarkup] = None, reply_markup: Optional[ReplyMarkup] = None,
business_connection_id: Optional[str] = None,
*, *,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None, reply_to_message_id: Optional[int] = None,
@ -4263,6 +4264,57 @@ class ExtBot(Bot, Generic[RLARGS]):
connect_timeout=connect_timeout, connect_timeout=connect_timeout,
pool_timeout=pool_timeout, pool_timeout=pool_timeout,
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
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,
rate_limit_args: Optional[RLARGS] = None,
) -> ChatInviteLink:
return await super().create_chat_subscription_invite_link(
chat_id=chat_id,
subscription_period=subscription_period,
subscription_price=subscription_price,
name=name,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
)
async def edit_chat_subscription_invite_link(
self,
chat_id: Union[str, int],
invite_link: Union[str, "ChatInviteLink"],
name: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
rate_limit_args: Optional[RLARGS] = None,
) -> ChatInviteLink:
return await super().edit_chat_subscription_invite_link(
chat_id=chat_id,
invite_link=invite_link,
name=name,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
) )
# updated camelCase aliases # updated camelCase aliases
@ -4388,4 +4440,6 @@ class ExtBot(Bot, Generic[RLARGS]):
replaceStickerInSet = replace_sticker_in_set replaceStickerInSet = replace_sticker_in_set
refundStarPayment = refund_star_payment refundStarPayment = refund_star_payment
getStarTransactions = get_star_transactions getStarTransactions = get_star_transactions
createChatSubscriptionInviteLink = create_chat_subscription_invite_link
editChatSubscriptionInviteLink = edit_chat_subscription_invite_link
sendPaidMedia = send_paid_media sendPaidMedia = send_paid_media

View file

@ -58,7 +58,7 @@ class CallbackQueryHandler(BaseHandler[Update, CCT]):
`~telegram.CallbackQuery.game_short_name` or :attr:`~telegram.CallbackQuery.data` `~telegram.CallbackQuery.game_short_name` or :attr:`~telegram.CallbackQuery.data`
matching the defined pattern will be handled matching the defined pattern will be handled
.. versionadded:: NEXT.VERSION .. versionadded:: 21.5
Warning: Warning:
When setting :paramref:`block` to :obj:`False`, you cannot rely on adding custom When setting :paramref:`block` to :obj:`False`, you cannot rely on adding custom
@ -100,7 +100,7 @@ class CallbackQueryHandler(BaseHandler[Update, CCT]):
:attr:`telegram.CallbackQuery.game_short_name` to determine if an update should be :attr:`telegram.CallbackQuery.game_short_name` to determine if an update should be
handled by this handler. handled by this handler.
.. versionadded:: NEXT.VERSION .. versionadded:: 21.5
block (:obj:`bool`, optional): Determines whether the return value of the callback should block (:obj:`bool`, optional): Determines whether the return value of the callback should
be awaited before processing the next handler in be awaited before processing the next handler in
:meth:`telegram.ext.Application.process_update`. Defaults to :obj:`True`. :meth:`telegram.ext.Application.process_update`. Defaults to :obj:`True`.

View file

@ -763,7 +763,6 @@ class Updater(AsyncContextManager["Updater"]):
_LOGGER.exception("Invalid token; aborting") _LOGGER.exception("Invalid token; aborting")
raise raise
except TelegramError as telegram_exc: except TelegramError as telegram_exc:
_LOGGER.exception("Error while %s:", description)
on_err_cb(telegram_exc) on_err_cb(telegram_exc)
# increase waiting times on subsequent errors up to 30secs # increase waiting times on subsequent errors up to 30secs

View file

@ -302,7 +302,7 @@ class BaseFilter:
class MessageFilter(BaseFilter): class MessageFilter(BaseFilter):
"""Base class for all Message Filters. In contrast to :class:`UpdateFilter`, the object passed """Base class for all Message Filters. In contrast to :class:`UpdateFilter`, the object passed
to :meth:`filter` is :obj:`telegram.Update.effective_message`. to :meth:`filter` is :attr:`telegram.Update.effective_message`.
Please see :class:`BaseFilter` for details on how to create custom filters. Please see :class:`BaseFilter` for details on how to create custom filters.

View file

@ -131,7 +131,7 @@ class RequestData:
def multipart_data(self) -> UploadFileDict: def multipart_data(self) -> UploadFileDict:
"""Gives the files contained in this object as mapping of part name to encoded content. """Gives the files contained in this object as mapping of part name to encoded content.
.. versionchanged:: NEXT.VERSION .. versionchanged:: 21.5
Content may now be a file handle. Content may now be a file handle.
""" """
multipart_data: UploadFileDict = {} multipart_data: UploadFileDict = {}

View file

@ -79,7 +79,7 @@ class RequestParameter:
def multipart_data(self) -> Optional[UploadFileDict]: def multipart_data(self) -> Optional[UploadFileDict]:
"""A dict with the file data to upload, if any. """A dict with the file data to upload, if any.
.. versionchanged:: NEXT.VERSION .. versionchanged:: 21.5
Content may now be a file handle. Content may now be a file handle.
""" """
if not self.input_files: if not self.input_files:

View file

@ -51,7 +51,7 @@ async def animation(bot, chat_id):
).animation ).animation
class TestAnimationBase: class AnimationTestBase:
animation_file_id = "CgADAQADngIAAuyVeEez0xRovKi9VAI" animation_file_id = "CgADAQADngIAAuyVeEez0xRovKi9VAI"
animation_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" animation_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
width = 320 width = 320
@ -66,7 +66,7 @@ class TestAnimationBase:
caption = "Test *animation*" caption = "Test *animation*"
class TestAnimationWithoutRequest(TestAnimationBase): class TestAnimationWithoutRequest(AnimationTestBase):
def test_slot_behaviour(self, animation): def test_slot_behaviour(self, animation):
for attr in animation.__slots__: for attr in animation.__slots__:
assert getattr(animation, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(animation, attr, "err") != "err", f"got extra slot '{attr}'"
@ -219,7 +219,7 @@ class TestAnimationWithoutRequest(TestAnimationBase):
) )
class TestAnimationWithRequest(TestAnimationBase): class TestAnimationWithRequest(AnimationTestBase):
async def test_send_all_args(self, bot, chat_id, animation_file, animation, thumb_file): async def test_send_all_args(self, bot, chat_id, animation_file, animation, thumb_file):
message = await bot.send_animation( message = await bot.send_animation(
chat_id, chat_id,

View file

@ -49,7 +49,7 @@ async def audio(bot, chat_id):
return (await bot.send_audio(chat_id, audio=f, read_timeout=50, thumbnail=thumb)).audio return (await bot.send_audio(chat_id, audio=f, read_timeout=50, thumbnail=thumb)).audio
class TestAudioBase: class AudioTestBase:
caption = "Test *audio*" caption = "Test *audio*"
performer = "Leandro Toledo" performer = "Leandro Toledo"
title = "Teste" title = "Teste"
@ -67,7 +67,7 @@ class TestAudioBase:
audio_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" audio_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestAudioWithoutRequest(TestAudioBase): class TestAudioWithoutRequest(AudioTestBase):
def test_slot_behaviour(self, audio): def test_slot_behaviour(self, audio):
for attr in audio.__slots__: for attr in audio.__slots__:
assert getattr(audio, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(audio, attr, "err") != "err", f"got extra slot '{attr}'"
@ -222,7 +222,7 @@ class TestAudioWithoutRequest(TestAudioBase):
await default_bot.send_audio(chat_id, audio, reply_parameters=ReplyParameters(**kwargs)) await default_bot.send_audio(chat_id, audio, reply_parameters=ReplyParameters(**kwargs))
class TestAudioWithRequest(TestAudioBase): class TestAudioWithRequest(AudioTestBase):
async def test_send_all_args(self, bot, chat_id, audio_file, thumb_file): async def test_send_all_args(self, bot, chat_id, audio_file, thumb_file):
message = await bot.send_audio( message = await bot.send_audio(
chat_id, chat_id,

View file

@ -52,7 +52,7 @@ async def chat_photo(bot, super_group_id):
) )
class TestChatPhotoBase: class ChatPhotoTestBase:
chatphoto_small_file_id = "smallCgADAQADngIAAuyVeEez0xRovKi9VAI" chatphoto_small_file_id = "smallCgADAQADngIAAuyVeEez0xRovKi9VAI"
chatphoto_big_file_id = "bigCgADAQADngIAAuyVeEez0xRovKi9VAI" chatphoto_big_file_id = "bigCgADAQADngIAAuyVeEez0xRovKi9VAI"
chatphoto_small_file_unique_id = "smalladc3145fd2e84d95b64d68eaa22aa33e" chatphoto_small_file_unique_id = "smalladc3145fd2e84d95b64d68eaa22aa33e"
@ -60,7 +60,7 @@ class TestChatPhotoBase:
chatphoto_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.jpg" chatphoto_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.jpg"
class TestChatPhotoWithoutRequest(TestChatPhotoBase): class TestChatPhotoWithoutRequest(ChatPhotoTestBase):
def test_slot_behaviour(self, chat_photo): def test_slot_behaviour(self, chat_photo):
for attr in chat_photo.__slots__: for attr in chat_photo.__slots__:
assert getattr(chat_photo, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(chat_photo, attr, "err") != "err", f"got extra slot '{attr}'"

View file

@ -32,21 +32,21 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def contact(): def contact():
return Contact( return Contact(
TestContactBase.phone_number, ContactTestBase.phone_number,
TestContactBase.first_name, ContactTestBase.first_name,
TestContactBase.last_name, ContactTestBase.last_name,
TestContactBase.user_id, ContactTestBase.user_id,
) )
class TestContactBase: class ContactTestBase:
phone_number = "+11234567890" phone_number = "+11234567890"
first_name = "Leandro" first_name = "Leandro"
last_name = "Toledo" last_name = "Toledo"
user_id = 23 user_id = 23
class TestContactWithoutRequest(TestContactBase): class TestContactWithoutRequest(ContactTestBase):
def test_slot_behaviour(self, contact): def test_slot_behaviour(self, contact):
for attr in contact.__slots__: for attr in contact.__slots__:
assert getattr(contact, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(contact, attr, "err") != "err", f"got extra slot '{attr}'"
@ -156,7 +156,7 @@ class TestContactWithoutRequest(TestContactBase):
) )
class TestContactWithRequest(TestContactBase): class TestContactWithRequest(ContactTestBase):
@pytest.mark.parametrize( @pytest.mark.parametrize(
("default_bot", "custom"), ("default_bot", "custom"),
[ [

View file

@ -49,7 +49,7 @@ async def document(bot, chat_id):
return (await bot.send_document(chat_id, document=f, read_timeout=50)).document return (await bot.send_document(chat_id, document=f, read_timeout=50)).document
class TestDocumentBase: class DocumentTestBase:
caption = "DocumentTest - *Caption*" caption = "DocumentTest - *Caption*"
document_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.gif" document_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.gif"
file_size = 12948 file_size = 12948
@ -62,7 +62,7 @@ class TestDocumentBase:
document_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" document_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestDocumentWithoutRequest(TestDocumentBase): class TestDocumentWithoutRequest(DocumentTestBase):
def test_slot_behaviour(self, document): def test_slot_behaviour(self, document):
for attr in document.__slots__: for attr in document.__slots__:
assert getattr(document, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(document, attr, "err") != "err", f"got extra slot '{attr}'"
@ -218,7 +218,7 @@ class TestDocumentWithoutRequest(TestDocumentBase):
assert await document.get_file() assert await document.get_file()
class TestDocumentWithRequest(TestDocumentBase): class TestDocumentWithRequest(DocumentTestBase):
async def test_error_send_empty_file(self, bot, chat_id): async def test_error_send_empty_file(self, bot, chat_id):
with Path(os.devnull).open("rb") as f, pytest.raises(TelegramError): with Path(os.devnull).open("rb") as f, pytest.raises(TelegramError):
await bot.send_document(chat_id=chat_id, document=f) await bot.send_document(chat_id=chat_id, document=f)

View file

@ -31,10 +31,10 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def file(bot): def file(bot):
file = File( file = File(
TestFileBase.file_id, FileTestBase.file_id,
TestFileBase.file_unique_id, FileTestBase.file_unique_id,
file_path=TestFileBase.file_path, file_path=FileTestBase.file_path,
file_size=TestFileBase.file_size, file_size=FileTestBase.file_size,
) )
file.set_bot(bot) file.set_bot(bot)
file._unfreeze() file._unfreeze()
@ -51,10 +51,10 @@ def encrypted_file(bot):
"Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=", "Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=",
) )
ef = File( ef = File(
TestFileBase.file_id, FileTestBase.file_id,
TestFileBase.file_unique_id, FileTestBase.file_unique_id,
TestFileBase.file_size, FileTestBase.file_size,
TestFileBase.file_path, FileTestBase.file_path,
) )
ef.set_bot(bot) ef.set_bot(bot)
ef.set_credentials(fc) ef.set_credentials(fc)
@ -69,9 +69,9 @@ def encrypted_local_file(bot):
"Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=", "Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=",
) )
ef = File( ef = File(
TestFileBase.file_id, FileTestBase.file_id,
TestFileBase.file_unique_id, FileTestBase.file_unique_id,
TestFileBase.file_size, FileTestBase.file_size,
file_path=str(data_file("image_encrypted.jpg")), file_path=str(data_file("image_encrypted.jpg")),
) )
ef.set_bot(bot) ef.set_bot(bot)
@ -82,16 +82,16 @@ def encrypted_local_file(bot):
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def local_file(bot): def local_file(bot):
file = File( file = File(
TestFileBase.file_id, FileTestBase.file_id,
TestFileBase.file_unique_id, FileTestBase.file_unique_id,
file_path=str(data_file("local_file.txt")), file_path=str(data_file("local_file.txt")),
file_size=TestFileBase.file_size, file_size=FileTestBase.file_size,
) )
file.set_bot(bot) file.set_bot(bot)
return file return file
class TestFileBase: class FileTestBase:
file_id = "NOTVALIDDOESNOTMATTER" file_id = "NOTVALIDDOESNOTMATTER"
file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
file_path = ( file_path = (
@ -101,7 +101,7 @@ class TestFileBase:
file_content = "Saint-Saëns".encode() # Intentionally contains unicode chars. file_content = "Saint-Saëns".encode() # Intentionally contains unicode chars.
class TestFileWithoutRequest(TestFileBase): class TestFileWithoutRequest(FileTestBase):
def test_slot_behaviour(self, file): def test_slot_behaviour(self, file):
for attr in file.__slots__: for attr in file.__slots__:
assert getattr(file, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(file, attr, "err") != "err", f"got extra slot '{attr}'"
@ -273,7 +273,7 @@ class TestFileWithoutRequest(TestFileBase):
assert buf2[: len(buf)] == buf assert buf2[: len(buf)] == buf
class TestFileWithRequest(TestFileBase): class TestFileWithRequest(FileTestBase):
async def test_error_get_empty_file_id(self, bot): async def test_error_get_empty_file_id(self, bot):
with pytest.raises(TelegramError): with pytest.raises(TelegramError):
await bot.get_file(file_id="") await bot.get_file(file_id="")

View file

@ -68,94 +68,94 @@ from .test_video import video, video_file # noqa: F401
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def input_media_video(class_thumb_file): def input_media_video(class_thumb_file):
return InputMediaVideo( return InputMediaVideo(
media=TestInputMediaVideoBase.media, media=InputMediaVideoTestBase.media,
caption=TestInputMediaVideoBase.caption, caption=InputMediaVideoTestBase.caption,
width=TestInputMediaVideoBase.width, width=InputMediaVideoTestBase.width,
height=TestInputMediaVideoBase.height, height=InputMediaVideoTestBase.height,
duration=TestInputMediaVideoBase.duration, duration=InputMediaVideoTestBase.duration,
parse_mode=TestInputMediaVideoBase.parse_mode, parse_mode=InputMediaVideoTestBase.parse_mode,
caption_entities=TestInputMediaVideoBase.caption_entities, caption_entities=InputMediaVideoTestBase.caption_entities,
thumbnail=class_thumb_file, thumbnail=class_thumb_file,
supports_streaming=TestInputMediaVideoBase.supports_streaming, supports_streaming=InputMediaVideoTestBase.supports_streaming,
has_spoiler=TestInputMediaVideoBase.has_spoiler, has_spoiler=InputMediaVideoTestBase.has_spoiler,
show_caption_above_media=TestInputMediaVideoBase.show_caption_above_media, show_caption_above_media=InputMediaVideoTestBase.show_caption_above_media,
) )
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def input_media_photo(): def input_media_photo():
return InputMediaPhoto( return InputMediaPhoto(
media=TestInputMediaPhotoBase.media, media=InputMediaPhotoTestBase.media,
caption=TestInputMediaPhotoBase.caption, caption=InputMediaPhotoTestBase.caption,
parse_mode=TestInputMediaPhotoBase.parse_mode, parse_mode=InputMediaPhotoTestBase.parse_mode,
caption_entities=TestInputMediaPhotoBase.caption_entities, caption_entities=InputMediaPhotoTestBase.caption_entities,
has_spoiler=TestInputMediaPhotoBase.has_spoiler, has_spoiler=InputMediaPhotoTestBase.has_spoiler,
show_caption_above_media=TestInputMediaPhotoBase.show_caption_above_media, show_caption_above_media=InputMediaPhotoTestBase.show_caption_above_media,
) )
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def input_media_animation(class_thumb_file): def input_media_animation(class_thumb_file):
return InputMediaAnimation( return InputMediaAnimation(
media=TestInputMediaAnimationBase.media, media=InputMediaAnimationTestBase.media,
caption=TestInputMediaAnimationBase.caption, caption=InputMediaAnimationTestBase.caption,
parse_mode=TestInputMediaAnimationBase.parse_mode, parse_mode=InputMediaAnimationTestBase.parse_mode,
caption_entities=TestInputMediaAnimationBase.caption_entities, caption_entities=InputMediaAnimationTestBase.caption_entities,
width=TestInputMediaAnimationBase.width, width=InputMediaAnimationTestBase.width,
height=TestInputMediaAnimationBase.height, height=InputMediaAnimationTestBase.height,
thumbnail=class_thumb_file, thumbnail=class_thumb_file,
duration=TestInputMediaAnimationBase.duration, duration=InputMediaAnimationTestBase.duration,
has_spoiler=TestInputMediaAnimationBase.has_spoiler, has_spoiler=InputMediaAnimationTestBase.has_spoiler,
show_caption_above_media=TestInputMediaAnimationBase.show_caption_above_media, show_caption_above_media=InputMediaAnimationTestBase.show_caption_above_media,
) )
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def input_media_audio(class_thumb_file): def input_media_audio(class_thumb_file):
return InputMediaAudio( return InputMediaAudio(
media=TestInputMediaAudioBase.media, media=InputMediaAudioTestBase.media,
caption=TestInputMediaAudioBase.caption, caption=InputMediaAudioTestBase.caption,
duration=TestInputMediaAudioBase.duration, duration=InputMediaAudioTestBase.duration,
performer=TestInputMediaAudioBase.performer, performer=InputMediaAudioTestBase.performer,
title=TestInputMediaAudioBase.title, title=InputMediaAudioTestBase.title,
thumbnail=class_thumb_file, thumbnail=class_thumb_file,
parse_mode=TestInputMediaAudioBase.parse_mode, parse_mode=InputMediaAudioTestBase.parse_mode,
caption_entities=TestInputMediaAudioBase.caption_entities, caption_entities=InputMediaAudioTestBase.caption_entities,
) )
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def input_media_document(class_thumb_file): def input_media_document(class_thumb_file):
return InputMediaDocument( return InputMediaDocument(
media=TestInputMediaDocumentBase.media, media=InputMediaDocumentTestBase.media,
caption=TestInputMediaDocumentBase.caption, caption=InputMediaDocumentTestBase.caption,
thumbnail=class_thumb_file, thumbnail=class_thumb_file,
parse_mode=TestInputMediaDocumentBase.parse_mode, parse_mode=InputMediaDocumentTestBase.parse_mode,
caption_entities=TestInputMediaDocumentBase.caption_entities, caption_entities=InputMediaDocumentTestBase.caption_entities,
disable_content_type_detection=TestInputMediaDocumentBase.disable_content_type_detection, disable_content_type_detection=InputMediaDocumentTestBase.disable_content_type_detection,
) )
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def input_paid_media_photo(): def input_paid_media_photo():
return InputPaidMediaPhoto( return InputPaidMediaPhoto(
media=TestInputMediaPhotoBase.media, media=InputMediaPhotoTestBase.media,
) )
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def input_paid_media_video(class_thumb_file): def input_paid_media_video(class_thumb_file):
return InputPaidMediaVideo( return InputPaidMediaVideo(
media=TestInputMediaVideoBase.media, media=InputMediaVideoTestBase.media,
thumbnail=class_thumb_file, thumbnail=class_thumb_file,
width=TestInputMediaVideoBase.width, width=InputMediaVideoTestBase.width,
height=TestInputMediaVideoBase.height, height=InputMediaVideoTestBase.height,
duration=TestInputMediaVideoBase.duration, duration=InputMediaVideoTestBase.duration,
supports_streaming=TestInputMediaVideoBase.supports_streaming, supports_streaming=InputMediaVideoTestBase.supports_streaming,
) )
class TestInputMediaVideoBase: class InputMediaVideoTestBase:
type_ = "video" type_ = "video"
media = "NOTAREALFILEID" media = "NOTAREALFILEID"
caption = "My Caption" caption = "My Caption"
@ -169,7 +169,7 @@ class TestInputMediaVideoBase:
show_caption_above_media = True show_caption_above_media = True
class TestInputMediaVideoWithoutRequest(TestInputMediaVideoBase): class TestInputMediaVideoWithoutRequest(InputMediaVideoTestBase):
def test_slot_behaviour(self, input_media_video): def test_slot_behaviour(self, input_media_video):
inst = input_media_video inst = input_media_video
for attr in inst.__slots__: for attr in inst.__slots__:
@ -258,7 +258,7 @@ class TestInputMediaVideoWithoutRequest(TestInputMediaVideoBase):
) )
class TestInputMediaPhotoBase: class InputMediaPhotoTestBase:
type_ = "photo" type_ = "photo"
media = "NOTAREALFILEID" media = "NOTAREALFILEID"
caption = "My Caption" caption = "My Caption"
@ -268,7 +268,7 @@ class TestInputMediaPhotoBase:
show_caption_above_media = True show_caption_above_media = True
class TestInputMediaPhotoWithoutRequest(TestInputMediaPhotoBase): class TestInputMediaPhotoWithoutRequest(InputMediaPhotoTestBase):
def test_slot_behaviour(self, input_media_photo): def test_slot_behaviour(self, input_media_photo):
inst = input_media_photo inst = input_media_photo
for attr in inst.__slots__: for attr in inst.__slots__:
@ -322,7 +322,7 @@ class TestInputMediaPhotoWithoutRequest(TestInputMediaPhotoBase):
assert input_media_photo.media == data_file("telegram.mp4").as_uri() assert input_media_photo.media == data_file("telegram.mp4").as_uri()
class TestInputMediaAnimationBase: class InputMediaAnimationTestBase:
type_ = "animation" type_ = "animation"
media = "NOTAREALFILEID" media = "NOTAREALFILEID"
caption = "My Caption" caption = "My Caption"
@ -335,7 +335,7 @@ class TestInputMediaAnimationBase:
show_caption_above_media = True show_caption_above_media = True
class TestInputMediaAnimationWithoutRequest(TestInputMediaAnimationBase): class TestInputMediaAnimationWithoutRequest(InputMediaAnimationTestBase):
def test_slot_behaviour(self, input_media_animation): def test_slot_behaviour(self, input_media_animation):
inst = input_media_animation inst = input_media_animation
for attr in inst.__slots__: for attr in inst.__slots__:
@ -396,7 +396,7 @@ class TestInputMediaAnimationWithoutRequest(TestInputMediaAnimationBase):
assert input_media_animation.thumbnail == data_file("telegram.jpg").as_uri() assert input_media_animation.thumbnail == data_file("telegram.jpg").as_uri()
class TestInputMediaAudioBase: class InputMediaAudioTestBase:
type_ = "audio" type_ = "audio"
media = "NOTAREALFILEID" media = "NOTAREALFILEID"
caption = "My Caption" caption = "My Caption"
@ -407,7 +407,7 @@ class TestInputMediaAudioBase:
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)] caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
class TestInputMediaAudioWithoutRequest(TestInputMediaAudioBase): class TestInputMediaAudioWithoutRequest(InputMediaAudioTestBase):
def test_slot_behaviour(self, input_media_audio): def test_slot_behaviour(self, input_media_audio):
inst = input_media_audio inst = input_media_audio
for attr in inst.__slots__: for attr in inst.__slots__:
@ -467,7 +467,7 @@ class TestInputMediaAudioWithoutRequest(TestInputMediaAudioBase):
assert input_media_audio.thumbnail == data_file("telegram.jpg").as_uri() assert input_media_audio.thumbnail == data_file("telegram.jpg").as_uri()
class TestInputMediaDocumentBase: class InputMediaDocumentTestBase:
type_ = "document" type_ = "document"
media = "NOTAREALFILEID" media = "NOTAREALFILEID"
caption = "My Caption" caption = "My Caption"
@ -476,7 +476,7 @@ class TestInputMediaDocumentBase:
disable_content_type_detection = True disable_content_type_detection = True
class TestInputMediaDocumentWithoutRequest(TestInputMediaDocumentBase): class TestInputMediaDocumentWithoutRequest(InputMediaDocumentTestBase):
def test_slot_behaviour(self, input_media_document): def test_slot_behaviour(self, input_media_document):
inst = input_media_document inst = input_media_document
for attr in inst.__slots__: for attr in inst.__slots__:
@ -535,7 +535,7 @@ class TestInputMediaDocumentWithoutRequest(TestInputMediaDocumentBase):
assert input_media_document.thumbnail == data_file("telegram.jpg").as_uri() assert input_media_document.thumbnail == data_file("telegram.jpg").as_uri()
class TestInputPaidMediaPhotoWithoutRequest(TestInputMediaPhotoBase): class TestInputPaidMediaPhotoWithoutRequest(InputMediaPhotoTestBase):
def test_slot_behaviour(self, input_paid_media_photo): def test_slot_behaviour(self, input_paid_media_photo):
inst = input_paid_media_photo inst = input_paid_media_photo
for attr in inst.__slots__: for attr in inst.__slots__:
@ -568,7 +568,7 @@ class TestInputPaidMediaPhotoWithoutRequest(TestInputMediaPhotoBase):
assert input_paid_media_photo.media == data_file("telegram.jpg").as_uri() assert input_paid_media_photo.media == data_file("telegram.jpg").as_uri()
class TestInputPaidMediaVideoWithoutRequest(TestInputMediaVideoBase): class TestInputPaidMediaVideoWithoutRequest(InputMediaVideoTestBase):
def test_slot_behaviour(self, input_paid_media_video): def test_slot_behaviour(self, input_paid_media_video):
inst = input_paid_media_video inst = input_paid_media_video
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -29,15 +29,15 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def input_sticker(): def input_sticker():
return InputSticker( return InputSticker(
sticker=TestInputStickerBase.sticker, sticker=InputStickerTestBase.sticker,
emoji_list=TestInputStickerBase.emoji_list, emoji_list=InputStickerTestBase.emoji_list,
mask_position=TestInputStickerBase.mask_position, mask_position=InputStickerTestBase.mask_position,
keywords=TestInputStickerBase.keywords, keywords=InputStickerTestBase.keywords,
format=TestInputStickerBase.format, format=InputStickerTestBase.format,
) )
class TestInputStickerBase: class InputStickerTestBase:
sticker = "fake_file_id" sticker = "fake_file_id"
emoji_list = ("👍", "👎") emoji_list = ("👍", "👎")
mask_position = MaskPosition("forehead", 0.5, 0.5, 0.5) mask_position = MaskPosition("forehead", 0.5, 0.5, 0.5)
@ -45,7 +45,7 @@ class TestInputStickerBase:
format = "static" format = "static"
class TestInputStickerWithoutRequest(TestInputStickerBase): class TestInputStickerWithoutRequest(InputStickerTestBase):
def test_slot_behaviour(self, input_sticker): def test_slot_behaviour(self, input_sticker):
inst = input_sticker inst = input_sticker
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -31,16 +31,16 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def location(): def location():
return Location( return Location(
latitude=TestLocationBase.latitude, latitude=LocationTestBase.latitude,
longitude=TestLocationBase.longitude, longitude=LocationTestBase.longitude,
horizontal_accuracy=TestLocationBase.horizontal_accuracy, horizontal_accuracy=LocationTestBase.horizontal_accuracy,
live_period=TestLocationBase.live_period, live_period=LocationTestBase.live_period,
heading=TestLocationBase.live_period, heading=LocationTestBase.live_period,
proximity_alert_radius=TestLocationBase.proximity_alert_radius, proximity_alert_radius=LocationTestBase.proximity_alert_radius,
) )
class TestLocationBase: class LocationTestBase:
latitude = -23.691288 latitude = -23.691288
longitude = -46.788279 longitude = -46.788279
horizontal_accuracy = 999 horizontal_accuracy = 999
@ -49,7 +49,7 @@ class TestLocationBase:
proximity_alert_radius = 50 proximity_alert_radius = 50
class TestLocationWithoutRequest(TestLocationBase): class TestLocationWithoutRequest(LocationTestBase):
def test_slot_behaviour(self, location): def test_slot_behaviour(self, location):
for attr in location.__slots__: for attr in location.__slots__:
assert getattr(location, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(location, attr, "err") != "err", f"got extra slot '{attr}'"

View file

@ -65,7 +65,7 @@ def photo(photolist):
return photolist[-1] return photolist[-1]
class TestPhotoBase: class PhotoTestBase:
width = 800 width = 800
height = 800 height = 800
caption = "<b>PhotoTest</b> - *Caption*" caption = "<b>PhotoTest</b> - *Caption*"
@ -75,7 +75,7 @@ class TestPhotoBase:
file_size = [29176, 27662] file_size = [29176, 27662]
class TestPhotoWithoutRequest(TestPhotoBase): class TestPhotoWithoutRequest(PhotoTestBase):
def test_slot_behaviour(self, photo): def test_slot_behaviour(self, photo):
for attr in photo.__slots__: for attr in photo.__slots__:
assert getattr(photo, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(photo, attr, "err") != "err", f"got extra slot '{attr}'"
@ -237,7 +237,7 @@ class TestPhotoWithoutRequest(TestPhotoBase):
await default_bot.send_photo(chat_id, photo, reply_parameters=ReplyParameters(**kwargs)) await default_bot.send_photo(chat_id, photo, reply_parameters=ReplyParameters(**kwargs))
class TestPhotoWithRequest(TestPhotoBase): class TestPhotoWithRequest(PhotoTestBase):
async def test_send_photo_all_args(self, bot, chat_id, photo_file): async def test_send_photo_all_args(self, bot, chat_id, photo_file):
message = await bot.send_photo( message = await bot.send_photo(
chat_id, chat_id,

View file

@ -61,7 +61,7 @@ async def sticker(bot, chat_id):
sticker = (await bot.send_sticker(chat_id, sticker=f, read_timeout=50)).sticker sticker = (await bot.send_sticker(chat_id, sticker=f, read_timeout=50)).sticker
# necessary to properly test needs_repainting # necessary to properly test needs_repainting
with sticker._unfrozen(): with sticker._unfrozen():
sticker.needs_repainting = TestStickerBase.needs_repainting sticker.needs_repainting = StickerTestBase.needs_repainting
return sticker return sticker
@ -89,7 +89,7 @@ def video_sticker(bot, chat_id):
return bot.send_sticker(chat_id, sticker=f, timeout=50).sticker return bot.send_sticker(chat_id, sticker=f, timeout=50).sticker
class TestStickerBase: class StickerTestBase:
# sticker_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.webp' # sticker_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.webp'
# Serving sticker from gh since our server sends wrong content_type # Serving sticker from gh since our server sends wrong content_type
sticker_file_url = ( sticker_file_url = (
@ -116,7 +116,7 @@ class TestStickerBase:
premium_animation = File("this_is_an_id", "this_is_an_unique_id") premium_animation = File("this_is_an_id", "this_is_an_unique_id")
class TestStickerWithoutRequest(TestStickerBase): class TestStickerWithoutRequest(StickerTestBase):
def test_slot_behaviour(self, sticker): def test_slot_behaviour(self, sticker):
for attr in sticker.__slots__: for attr in sticker.__slots__:
assert getattr(sticker, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(sticker, attr, "err") != "err", f"got extra slot '{attr}'"
@ -345,7 +345,7 @@ class TestStickerWithoutRequest(TestStickerBase):
) )
class TestStickerWithRequest(TestStickerBase): class TestStickerWithRequest(StickerTestBase):
async def test_send_all_args(self, bot, chat_id, sticker_file, sticker): async def test_send_all_args(self, bot, chat_id, sticker_file, sticker):
message = await bot.send_sticker( message = await bot.send_sticker(
chat_id, sticker=sticker_file, disable_notification=False, protect_content=True chat_id, sticker=sticker_file, disable_notification=False, protect_content=True
@ -572,7 +572,7 @@ def sticker_set_thumb_file():
yield file yield file
class TestStickerSetBase: class StickerSetTestBase:
title = "Test stickers" title = "Test stickers"
stickers = [Sticker("file_id", "file_un_id", 512, 512, True, True, Sticker.REGULAR)] stickers = [Sticker("file_id", "file_un_id", 512, 512, True, True, Sticker.REGULAR)]
name = "NOTAREALNAME" name = "NOTAREALNAME"
@ -580,7 +580,7 @@ class TestStickerSetBase:
contains_masks = True contains_masks = True
class TestStickerSetWithoutRequest(TestStickerSetBase): class TestStickerSetWithoutRequest(StickerSetTestBase):
def test_slot_behaviour(self): def test_slot_behaviour(self):
inst = StickerSet("this", "is", self.stickers, "not") inst = StickerSet("this", "is", self.stickers, "not")
for attr in inst.__slots__: for attr in inst.__slots__:
@ -1064,21 +1064,21 @@ class TestStickerSetWithRequest:
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def mask_position(): def mask_position():
return MaskPosition( return MaskPosition(
TestMaskPositionBase.point, MaskPositionTestBase.point,
TestMaskPositionBase.x_shift, MaskPositionTestBase.x_shift,
TestMaskPositionBase.y_shift, MaskPositionTestBase.y_shift,
TestMaskPositionBase.scale, MaskPositionTestBase.scale,
) )
class TestMaskPositionBase: class MaskPositionTestBase:
point = MaskPosition.EYES point = MaskPosition.EYES
x_shift = -1 x_shift = -1
y_shift = 1 y_shift = 1
scale = 2 scale = 2
class TestMaskPositionWithoutRequest(TestMaskPositionBase): class TestMaskPositionWithoutRequest(MaskPositionTestBase):
def test_slot_behaviour(self, mask_position): def test_slot_behaviour(self, mask_position):
inst = mask_position inst = mask_position
for attr in inst.__slots__: for attr in inst.__slots__:
@ -1130,7 +1130,7 @@ class TestMaskPositionWithoutRequest(TestMaskPositionBase):
assert hash(a) != hash(e) assert hash(a) != hash(e)
class TestMaskPositionWithRequest(TestMaskPositionBase): class TestMaskPositionWithRequest(MaskPositionTestBase):
async def test_create_new_mask_sticker_set(self, bot, chat_id, sticker_file, mask_position): async def test_create_new_mask_sticker_set(self, bot, chat_id, sticker_file, mask_position):
name = f"masks_by_{bot.username}" name = f"masks_by_{bot.username}"
try: try:

View file

@ -31,17 +31,17 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def venue(): def venue():
return Venue( return Venue(
TestVenueBase.location, VenueTestBase.location,
TestVenueBase.title, VenueTestBase.title,
TestVenueBase.address, VenueTestBase.address,
foursquare_id=TestVenueBase.foursquare_id, foursquare_id=VenueTestBase.foursquare_id,
foursquare_type=TestVenueBase.foursquare_type, foursquare_type=VenueTestBase.foursquare_type,
google_place_id=TestVenueBase.google_place_id, google_place_id=VenueTestBase.google_place_id,
google_place_type=TestVenueBase.google_place_type, google_place_type=VenueTestBase.google_place_type,
) )
class TestVenueBase: class VenueTestBase:
location = Location(longitude=-46.788279, latitude=-23.691288) location = Location(longitude=-46.788279, latitude=-23.691288)
title = "title" title = "title"
address = "address" address = "address"
@ -51,7 +51,7 @@ class TestVenueBase:
google_place_type = "google place type" google_place_type = "google place type"
class TestVenueWithoutRequest(TestVenueBase): class TestVenueWithoutRequest(VenueTestBase):
def test_slot_behaviour(self, venue): def test_slot_behaviour(self, venue):
for attr in venue.__slots__: for attr in venue.__slots__:
assert getattr(venue, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(venue, attr, "err") != "err", f"got extra slot '{attr}'"
@ -171,7 +171,7 @@ class TestVenueWithoutRequest(TestVenueBase):
) )
class TestVenueWithRequest(TestVenueBase): class TestVenueWithRequest(VenueTestBase):
@pytest.mark.parametrize( @pytest.mark.parametrize(
("default_bot", "custom"), ("default_bot", "custom"),
[ [

View file

@ -49,7 +49,7 @@ async def video(bot, chat_id):
return (await bot.send_video(chat_id, video=f, read_timeout=50)).video return (await bot.send_video(chat_id, video=f, read_timeout=50)).video
class TestVideoBase: class VideoTestBase:
width = 360 width = 360
height = 640 height = 640
duration = 5 duration = 5
@ -66,7 +66,7 @@ class TestVideoBase:
video_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" video_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestVideoWithoutRequest(TestVideoBase): class TestVideoWithoutRequest(VideoTestBase):
def test_slot_behaviour(self, video): def test_slot_behaviour(self, video):
for attr in video.__slots__: for attr in video.__slots__:
assert getattr(video, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(video, attr, "err") != "err", f"got extra slot '{attr}'"
@ -229,7 +229,7 @@ class TestVideoWithoutRequest(TestVideoBase):
await default_bot.send_video(chat_id, video, reply_parameters=ReplyParameters(**kwargs)) await default_bot.send_video(chat_id, video, reply_parameters=ReplyParameters(**kwargs))
class TestVideoWithRequest(TestVideoBase): class TestVideoWithRequest(VideoTestBase):
async def test_send_all_args(self, bot, chat_id, video_file, video, thumb_file): async def test_send_all_args(self, bot, chat_id, video_file, video, thumb_file):
message = await bot.send_video( message = await bot.send_video(
chat_id, chat_id,

View file

@ -48,7 +48,7 @@ async def video_note(bot, chat_id):
return (await bot.send_video_note(chat_id, video_note=f, read_timeout=50)).video_note return (await bot.send_video_note(chat_id, video_note=f, read_timeout=50)).video_note
class TestVideoNoteBase: class VideoNoteTestBase:
length = 240 length = 240
duration = 3 duration = 3
file_size = 132084 file_size = 132084
@ -60,7 +60,7 @@ class TestVideoNoteBase:
videonote_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" videonote_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestVideoNoteWithoutRequest(TestVideoNoteBase): class TestVideoNoteWithoutRequest(VideoNoteTestBase):
def test_slot_behaviour(self, video_note): def test_slot_behaviour(self, video_note):
for attr in video_note.__slots__: for attr in video_note.__slots__:
assert getattr(video_note, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(video_note, attr, "err") != "err", f"got extra slot '{attr}'"
@ -218,7 +218,7 @@ class TestVideoNoteWithoutRequest(TestVideoNoteBase):
) )
class TestVideoNoteWithRequest(TestVideoNoteBase): class TestVideoNoteWithRequest(VideoNoteTestBase):
async def test_send_all_args(self, bot, chat_id, video_note_file, video_note, thumb_file): async def test_send_all_args(self, bot, chat_id, video_note_file, video_note, thumb_file):
message = await bot.send_video_note( message = await bot.send_video_note(
chat_id, chat_id,

View file

@ -49,7 +49,7 @@ async def voice(bot, chat_id):
return (await bot.send_voice(chat_id, voice=f, read_timeout=50)).voice return (await bot.send_voice(chat_id, voice=f, read_timeout=50)).voice
class TestVoiceBase: class VoiceTestBase:
duration = 3 duration = 3
mime_type = "audio/ogg" mime_type = "audio/ogg"
file_size = 9199 file_size = 9199
@ -59,7 +59,7 @@ class TestVoiceBase:
voice_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" voice_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestVoiceWithoutRequest(TestVoiceBase): class TestVoiceWithoutRequest(VoiceTestBase):
def test_slot_behaviour(self, voice): def test_slot_behaviour(self, voice):
for attr in voice.__slots__: for attr in voice.__slots__:
assert getattr(voice, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(voice, attr, "err") != "err", f"got extra slot '{attr}'"
@ -203,7 +203,7 @@ class TestVoiceWithoutRequest(TestVoiceBase):
await default_bot.send_voice(chat_id, voice, reply_parameters=ReplyParameters(**kwargs)) await default_bot.send_voice(chat_id, voice, reply_parameters=ReplyParameters(**kwargs))
class TestVoiceWithRequest(TestVoiceBase): class TestVoiceWithRequest(VoiceTestBase):
async def test_send_all_args(self, bot, chat_id, voice_file, voice): async def test_send_all_args(self, bot, chat_id, voice_file, voice):
message = await bot.send_voice( message = await bot.send_voice(
chat_id, chat_id,

View file

@ -26,18 +26,18 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def game(): def game():
game = Game( game = Game(
TestGameBase.title, GameTestBase.title,
TestGameBase.description, GameTestBase.description,
TestGameBase.photo, GameTestBase.photo,
text=TestGameBase.text, text=GameTestBase.text,
text_entities=TestGameBase.text_entities, text_entities=GameTestBase.text_entities,
animation=TestGameBase.animation, animation=GameTestBase.animation,
) )
game._unfreeze() game._unfreeze()
return game return game
class TestGameBase: class GameTestBase:
title = "Python-telegram-bot Test Game" title = "Python-telegram-bot Test Game"
description = "description" description = "description"
photo = [PhotoSize("Blah", "ElseBlah", 640, 360, file_size=0)] photo = [PhotoSize("Blah", "ElseBlah", 640, 360, file_size=0)]
@ -49,7 +49,7 @@ class TestGameBase:
animation = Animation("blah", "unique_id", 320, 180, 1) animation = Animation("blah", "unique_id", 320, 180, 1)
class TestGameWithoutRequest(TestGameBase): class TestGameWithoutRequest(GameTestBase):
def test_slot_behaviour(self, game): def test_slot_behaviour(self, game):
for attr in game.__slots__: for attr in game.__slots__:
assert getattr(game, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(game, attr, "err") != "err", f"got extra slot '{attr}'"

View file

@ -26,17 +26,17 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def game_highscore(): def game_highscore():
return GameHighScore( return GameHighScore(
TestGameHighScoreBase.position, TestGameHighScoreBase.user, TestGameHighScoreBase.score GameHighScoreTestBase.position, GameHighScoreTestBase.user, GameHighScoreTestBase.score
) )
class TestGameHighScoreBase: class GameHighScoreTestBase:
position = 12 position = 12
user = User(2, "test user", False) user = User(2, "test user", False)
score = 42 score = 42
class TestGameHighScoreWithoutRequest(TestGameHighScoreBase): class TestGameHighScoreWithoutRequest(GameHighScoreTestBase):
def test_slot_behaviour(self, game_highscore): def test_slot_behaviour(self, game_highscore):
for attr in game_highscore.__slots__: for attr in game_highscore.__slots__:
assert getattr(game_highscore, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(game_highscore, attr, "err") != "err", f"got extra slot '{attr}'"

View file

@ -32,24 +32,24 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_keyboard_button(): def inline_keyboard_button():
return InlineKeyboardButton( return InlineKeyboardButton(
TestInlineKeyboardButtonBase.text, InlineKeyboardButtonTestBase.text,
url=TestInlineKeyboardButtonBase.url, url=InlineKeyboardButtonTestBase.url,
callback_data=TestInlineKeyboardButtonBase.callback_data, callback_data=InlineKeyboardButtonTestBase.callback_data,
switch_inline_query=TestInlineKeyboardButtonBase.switch_inline_query, switch_inline_query=InlineKeyboardButtonTestBase.switch_inline_query,
switch_inline_query_current_chat=( switch_inline_query_current_chat=(
TestInlineKeyboardButtonBase.switch_inline_query_current_chat InlineKeyboardButtonTestBase.switch_inline_query_current_chat
), ),
callback_game=TestInlineKeyboardButtonBase.callback_game, callback_game=InlineKeyboardButtonTestBase.callback_game,
pay=TestInlineKeyboardButtonBase.pay, pay=InlineKeyboardButtonTestBase.pay,
login_url=TestInlineKeyboardButtonBase.login_url, login_url=InlineKeyboardButtonTestBase.login_url,
web_app=TestInlineKeyboardButtonBase.web_app, web_app=InlineKeyboardButtonTestBase.web_app,
switch_inline_query_chosen_chat=( switch_inline_query_chosen_chat=(
TestInlineKeyboardButtonBase.switch_inline_query_chosen_chat InlineKeyboardButtonTestBase.switch_inline_query_chosen_chat
), ),
) )
class TestInlineKeyboardButtonBase: class InlineKeyboardButtonTestBase:
text = "text" text = "text"
url = "url" url = "url"
callback_data = "callback data" callback_data = "callback data"
@ -62,7 +62,7 @@ class TestInlineKeyboardButtonBase:
switch_inline_query_chosen_chat = SwitchInlineQueryChosenChat("a_bot", True, False, True, True) switch_inline_query_chosen_chat = SwitchInlineQueryChosenChat("a_bot", True, False, True, True)
class TestInlineKeyboardButtonWithoutRequest(TestInlineKeyboardButtonBase): class TestInlineKeyboardButtonWithoutRequest(InlineKeyboardButtonTestBase):
def test_slot_behaviour(self, inline_keyboard_button): def test_slot_behaviour(self, inline_keyboard_button):
inst = inline_keyboard_button inst = inline_keyboard_button
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -31,10 +31,10 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_keyboard_markup(): def inline_keyboard_markup():
return InlineKeyboardMarkup(TestInlineKeyboardMarkupBase.inline_keyboard) return InlineKeyboardMarkup(InlineKeyboardMarkupTestBase.inline_keyboard)
class TestInlineKeyboardMarkupBase: class InlineKeyboardMarkupTestBase:
inline_keyboard = [ inline_keyboard = [
[ [
InlineKeyboardButton(text="button1", callback_data="data1"), InlineKeyboardButton(text="button1", callback_data="data1"),
@ -43,7 +43,7 @@ class TestInlineKeyboardMarkupBase:
] ]
class TestInlineKeyboardMarkupWithoutRequest(TestInlineKeyboardMarkupBase): class TestInlineKeyboardMarkupWithoutRequest(InlineKeyboardMarkupTestBase):
def test_slot_behaviour(self, inline_keyboard_markup): def test_slot_behaviour(self, inline_keyboard_markup):
inst = inline_keyboard_markup inst = inline_keyboard_markup
for attr in inst.__slots__: for attr in inst.__slots__:
@ -228,7 +228,7 @@ class TestInlineKeyboardMarkupWithoutRequest(TestInlineKeyboardMarkupBase):
await bot.send_message(123, "test", reply_markup=inline_keyboard_markup) await bot.send_message(123, "test", reply_markup=inline_keyboard_markup)
class TestInlineKeyborardMarkupWithRequest(TestInlineKeyboardMarkupBase): class TestInlineKeyborardMarkupWithRequest(InlineKeyboardMarkupTestBase):
async def test_send_message_with_inline_keyboard_markup( async def test_send_message_with_inline_keyboard_markup(
self, bot, chat_id, inline_keyboard_markup self, bot, chat_id, inline_keyboard_markup
): ):

View file

@ -31,17 +31,17 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query(bot): def inline_query(bot):
ilq = InlineQuery( ilq = InlineQuery(
TestInlineQueryBase.id_, InlineQueryTestBase.id_,
TestInlineQueryBase.from_user, InlineQueryTestBase.from_user,
TestInlineQueryBase.query, InlineQueryTestBase.query,
TestInlineQueryBase.offset, InlineQueryTestBase.offset,
location=TestInlineQueryBase.location, location=InlineQueryTestBase.location,
) )
ilq.set_bot(bot) ilq.set_bot(bot)
return ilq return ilq
class TestInlineQueryBase: class InlineQueryTestBase:
id_ = 1234 id_ = 1234
from_user = User(1, "First name", False) from_user = User(1, "First name", False)
query = "query text" query = "query text"
@ -49,7 +49,7 @@ class TestInlineQueryBase:
location = Location(8.8, 53.1) location = Location(8.8, 53.1)
class TestInlineQueryWithoutRequest(TestInlineQueryBase): class TestInlineQueryWithoutRequest(InlineQueryTestBase):
def test_slot_behaviour(self, inline_query): def test_slot_behaviour(self, inline_query):
for attr in inline_query.__slots__: for attr in inline_query.__slots__:
assert getattr(inline_query, attr, "err") != "err", f"got extra slot '{attr}'" assert getattr(inline_query, attr, "err") != "err", f"got extra slot '{attr}'"

View file

@ -34,20 +34,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_article(): def inline_query_result_article():
return InlineQueryResultArticle( return InlineQueryResultArticle(
TestInlineQueryResultArticleBase.id_, InlineQueryResultArticleTestBase.id_,
TestInlineQueryResultArticleBase.title, InlineQueryResultArticleTestBase.title,
input_message_content=TestInlineQueryResultArticleBase.input_message_content, input_message_content=InlineQueryResultArticleTestBase.input_message_content,
reply_markup=TestInlineQueryResultArticleBase.reply_markup, reply_markup=InlineQueryResultArticleTestBase.reply_markup,
url=TestInlineQueryResultArticleBase.url, url=InlineQueryResultArticleTestBase.url,
hide_url=TestInlineQueryResultArticleBase.hide_url, hide_url=InlineQueryResultArticleTestBase.hide_url,
description=TestInlineQueryResultArticleBase.description, description=InlineQueryResultArticleTestBase.description,
thumbnail_url=TestInlineQueryResultArticleBase.thumbnail_url, thumbnail_url=InlineQueryResultArticleTestBase.thumbnail_url,
thumbnail_height=TestInlineQueryResultArticleBase.thumbnail_height, thumbnail_height=InlineQueryResultArticleTestBase.thumbnail_height,
thumbnail_width=TestInlineQueryResultArticleBase.thumbnail_width, thumbnail_width=InlineQueryResultArticleTestBase.thumbnail_width,
) )
class TestInlineQueryResultArticleBase: class InlineQueryResultArticleTestBase:
id_ = "id" id_ = "id"
type_ = "article" type_ = "article"
title = "title" title = "title"
@ -61,7 +61,7 @@ class TestInlineQueryResultArticleBase:
thumbnail_width = 15 thumbnail_width = 15
class TestInlineQueryResultArticleWithoutRequest(TestInlineQueryResultArticleBase): class TestInlineQueryResultArticleWithoutRequest(InlineQueryResultArticleTestBase):
def test_slot_behaviour(self, inline_query_result_article): def test_slot_behaviour(self, inline_query_result_article):
inst = inline_query_result_article inst = inline_query_result_article
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -33,20 +33,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_audio(): def inline_query_result_audio():
return InlineQueryResultAudio( return InlineQueryResultAudio(
TestInlineQueryResultAudioBase.id_, InlineQueryResultAudioTestBase.id_,
TestInlineQueryResultAudioBase.audio_url, InlineQueryResultAudioTestBase.audio_url,
TestInlineQueryResultAudioBase.title, InlineQueryResultAudioTestBase.title,
performer=TestInlineQueryResultAudioBase.performer, performer=InlineQueryResultAudioTestBase.performer,
audio_duration=TestInlineQueryResultAudioBase.audio_duration, audio_duration=InlineQueryResultAudioTestBase.audio_duration,
caption=TestInlineQueryResultAudioBase.caption, caption=InlineQueryResultAudioTestBase.caption,
parse_mode=TestInlineQueryResultAudioBase.parse_mode, parse_mode=InlineQueryResultAudioTestBase.parse_mode,
caption_entities=TestInlineQueryResultAudioBase.caption_entities, caption_entities=InlineQueryResultAudioTestBase.caption_entities,
input_message_content=TestInlineQueryResultAudioBase.input_message_content, input_message_content=InlineQueryResultAudioTestBase.input_message_content,
reply_markup=TestInlineQueryResultAudioBase.reply_markup, reply_markup=InlineQueryResultAudioTestBase.reply_markup,
) )
class TestInlineQueryResultAudioBase: class InlineQueryResultAudioTestBase:
id_ = "id" id_ = "id"
type_ = "audio" type_ = "audio"
audio_url = "audio url" audio_url = "audio url"
@ -60,7 +60,7 @@ class TestInlineQueryResultAudioBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultAudioWithoutRequest(TestInlineQueryResultAudioBase): class TestInlineQueryResultAudioWithoutRequest(InlineQueryResultAudioTestBase):
def test_slot_behaviour(self, inline_query_result_audio): def test_slot_behaviour(self, inline_query_result_audio):
inst = inline_query_result_audio inst = inline_query_result_audio
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -33,17 +33,17 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_cached_audio(): def inline_query_result_cached_audio():
return InlineQueryResultCachedAudio( return InlineQueryResultCachedAudio(
TestInlineQueryResultCachedAudioBase.id_, InlineQueryResultCachedAudioTestBase.id_,
TestInlineQueryResultCachedAudioBase.audio_file_id, InlineQueryResultCachedAudioTestBase.audio_file_id,
caption=TestInlineQueryResultCachedAudioBase.caption, caption=InlineQueryResultCachedAudioTestBase.caption,
parse_mode=TestInlineQueryResultCachedAudioBase.parse_mode, parse_mode=InlineQueryResultCachedAudioTestBase.parse_mode,
caption_entities=TestInlineQueryResultCachedAudioBase.caption_entities, caption_entities=InlineQueryResultCachedAudioTestBase.caption_entities,
input_message_content=TestInlineQueryResultCachedAudioBase.input_message_content, input_message_content=InlineQueryResultCachedAudioTestBase.input_message_content,
reply_markup=TestInlineQueryResultCachedAudioBase.reply_markup, reply_markup=InlineQueryResultCachedAudioTestBase.reply_markup,
) )
class TestInlineQueryResultCachedAudioBase: class InlineQueryResultCachedAudioTestBase:
id_ = "id" id_ = "id"
type_ = "audio" type_ = "audio"
audio_file_id = "audio file id" audio_file_id = "audio file id"
@ -54,7 +54,7 @@ class TestInlineQueryResultCachedAudioBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedAudioWithoutRequest(TestInlineQueryResultCachedAudioBase): class TestInlineQueryResultCachedAudioWithoutRequest(InlineQueryResultCachedAudioTestBase):
def test_slot_behaviour(self, inline_query_result_cached_audio): def test_slot_behaviour(self, inline_query_result_cached_audio):
inst = inline_query_result_cached_audio inst = inline_query_result_cached_audio
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -33,19 +33,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_cached_document(): def inline_query_result_cached_document():
return InlineQueryResultCachedDocument( return InlineQueryResultCachedDocument(
TestInlineQueryResultCachedDocumentBase.id_, InlineQueryResultCachedDocumentTestBase.id_,
TestInlineQueryResultCachedDocumentBase.title, InlineQueryResultCachedDocumentTestBase.title,
TestInlineQueryResultCachedDocumentBase.document_file_id, InlineQueryResultCachedDocumentTestBase.document_file_id,
caption=TestInlineQueryResultCachedDocumentBase.caption, caption=InlineQueryResultCachedDocumentTestBase.caption,
parse_mode=TestInlineQueryResultCachedDocumentBase.parse_mode, parse_mode=InlineQueryResultCachedDocumentTestBase.parse_mode,
caption_entities=TestInlineQueryResultCachedDocumentBase.caption_entities, caption_entities=InlineQueryResultCachedDocumentTestBase.caption_entities,
description=TestInlineQueryResultCachedDocumentBase.description, description=InlineQueryResultCachedDocumentTestBase.description,
input_message_content=TestInlineQueryResultCachedDocumentBase.input_message_content, input_message_content=InlineQueryResultCachedDocumentTestBase.input_message_content,
reply_markup=TestInlineQueryResultCachedDocumentBase.reply_markup, reply_markup=InlineQueryResultCachedDocumentTestBase.reply_markup,
) )
class TestInlineQueryResultCachedDocumentBase: class InlineQueryResultCachedDocumentTestBase:
id_ = "id" id_ = "id"
type_ = "document" type_ = "document"
document_file_id = "document file id" document_file_id = "document file id"
@ -58,7 +58,7 @@ class TestInlineQueryResultCachedDocumentBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedDocumentWithoutRequest(TestInlineQueryResultCachedDocumentBase): class TestInlineQueryResultCachedDocumentWithoutRequest(InlineQueryResultCachedDocumentTestBase):
def test_slot_behaviour(self, inline_query_result_cached_document): def test_slot_behaviour(self, inline_query_result_cached_document):
inst = inline_query_result_cached_document inst = inline_query_result_cached_document
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -32,19 +32,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_cached_gif(): def inline_query_result_cached_gif():
return InlineQueryResultCachedGif( return InlineQueryResultCachedGif(
TestInlineQueryResultCachedGifBase.id_, InlineQueryResultCachedGifTestBase.id_,
TestInlineQueryResultCachedGifBase.gif_file_id, InlineQueryResultCachedGifTestBase.gif_file_id,
title=TestInlineQueryResultCachedGifBase.title, title=InlineQueryResultCachedGifTestBase.title,
caption=TestInlineQueryResultCachedGifBase.caption, caption=InlineQueryResultCachedGifTestBase.caption,
parse_mode=TestInlineQueryResultCachedGifBase.parse_mode, parse_mode=InlineQueryResultCachedGifTestBase.parse_mode,
caption_entities=TestInlineQueryResultCachedGifBase.caption_entities, caption_entities=InlineQueryResultCachedGifTestBase.caption_entities,
input_message_content=TestInlineQueryResultCachedGifBase.input_message_content, input_message_content=InlineQueryResultCachedGifTestBase.input_message_content,
reply_markup=TestInlineQueryResultCachedGifBase.reply_markup, reply_markup=InlineQueryResultCachedGifTestBase.reply_markup,
show_caption_above_media=TestInlineQueryResultCachedGifBase.show_caption_above_media, show_caption_above_media=InlineQueryResultCachedGifTestBase.show_caption_above_media,
) )
class TestInlineQueryResultCachedGifBase: class InlineQueryResultCachedGifTestBase:
id_ = "id" id_ = "id"
type_ = "gif" type_ = "gif"
gif_file_id = "gif file id" gif_file_id = "gif file id"
@ -57,7 +57,7 @@ class TestInlineQueryResultCachedGifBase:
show_caption_above_media = True show_caption_above_media = True
class TestInlineQueryResultCachedGifWithoutRequest(TestInlineQueryResultCachedGifBase): class TestInlineQueryResultCachedGifWithoutRequest(InlineQueryResultCachedGifTestBase):
def test_slot_behaviour(self, inline_query_result_cached_gif): def test_slot_behaviour(self, inline_query_result_cached_gif):
inst = inline_query_result_cached_gif inst = inline_query_result_cached_gif
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -32,19 +32,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_cached_mpeg4_gif(): def inline_query_result_cached_mpeg4_gif():
return InlineQueryResultCachedMpeg4Gif( return InlineQueryResultCachedMpeg4Gif(
TestInlineQueryResultCachedMpeg4GifBase.id_, InlineQueryResultCachedMpeg4GifTestBase.id_,
TestInlineQueryResultCachedMpeg4GifBase.mpeg4_file_id, InlineQueryResultCachedMpeg4GifTestBase.mpeg4_file_id,
title=TestInlineQueryResultCachedMpeg4GifBase.title, title=InlineQueryResultCachedMpeg4GifTestBase.title,
caption=TestInlineQueryResultCachedMpeg4GifBase.caption, caption=InlineQueryResultCachedMpeg4GifTestBase.caption,
parse_mode=TestInlineQueryResultCachedMpeg4GifBase.parse_mode, parse_mode=InlineQueryResultCachedMpeg4GifTestBase.parse_mode,
caption_entities=TestInlineQueryResultCachedMpeg4GifBase.caption_entities, caption_entities=InlineQueryResultCachedMpeg4GifTestBase.caption_entities,
input_message_content=TestInlineQueryResultCachedMpeg4GifBase.input_message_content, input_message_content=InlineQueryResultCachedMpeg4GifTestBase.input_message_content,
reply_markup=TestInlineQueryResultCachedMpeg4GifBase.reply_markup, reply_markup=InlineQueryResultCachedMpeg4GifTestBase.reply_markup,
show_caption_above_media=TestInlineQueryResultCachedMpeg4GifBase.show_caption_above_media, show_caption_above_media=InlineQueryResultCachedMpeg4GifTestBase.show_caption_above_media,
) )
class TestInlineQueryResultCachedMpeg4GifBase: class InlineQueryResultCachedMpeg4GifTestBase:
id_ = "id" id_ = "id"
type_ = "mpeg4_gif" type_ = "mpeg4_gif"
mpeg4_file_id = "mpeg4 file id" mpeg4_file_id = "mpeg4 file id"
@ -57,7 +57,7 @@ class TestInlineQueryResultCachedMpeg4GifBase:
show_caption_above_media = True show_caption_above_media = True
class TestInlineQueryResultCachedMpeg4GifWithoutRequest(TestInlineQueryResultCachedMpeg4GifBase): class TestInlineQueryResultCachedMpeg4GifWithoutRequest(InlineQueryResultCachedMpeg4GifTestBase):
def test_slot_behaviour(self, inline_query_result_cached_mpeg4_gif): def test_slot_behaviour(self, inline_query_result_cached_mpeg4_gif):
inst = inline_query_result_cached_mpeg4_gif inst = inline_query_result_cached_mpeg4_gif
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -32,20 +32,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_cached_photo(): def inline_query_result_cached_photo():
return InlineQueryResultCachedPhoto( return InlineQueryResultCachedPhoto(
TestInlineQueryResultCachedPhotoBase.id_, InlineQueryResultCachedPhotoTestBase.id_,
TestInlineQueryResultCachedPhotoBase.photo_file_id, InlineQueryResultCachedPhotoTestBase.photo_file_id,
title=TestInlineQueryResultCachedPhotoBase.title, title=InlineQueryResultCachedPhotoTestBase.title,
description=TestInlineQueryResultCachedPhotoBase.description, description=InlineQueryResultCachedPhotoTestBase.description,
caption=TestInlineQueryResultCachedPhotoBase.caption, caption=InlineQueryResultCachedPhotoTestBase.caption,
parse_mode=TestInlineQueryResultCachedPhotoBase.parse_mode, parse_mode=InlineQueryResultCachedPhotoTestBase.parse_mode,
caption_entities=TestInlineQueryResultCachedPhotoBase.caption_entities, caption_entities=InlineQueryResultCachedPhotoTestBase.caption_entities,
input_message_content=TestInlineQueryResultCachedPhotoBase.input_message_content, input_message_content=InlineQueryResultCachedPhotoTestBase.input_message_content,
reply_markup=TestInlineQueryResultCachedPhotoBase.reply_markup, reply_markup=InlineQueryResultCachedPhotoTestBase.reply_markup,
show_caption_above_media=TestInlineQueryResultCachedPhotoBase.show_caption_above_media, show_caption_above_media=InlineQueryResultCachedPhotoTestBase.show_caption_above_media,
) )
class TestInlineQueryResultCachedPhotoBase: class InlineQueryResultCachedPhotoTestBase:
id_ = "id" id_ = "id"
type_ = "photo" type_ = "photo"
photo_file_id = "photo file id" photo_file_id = "photo file id"
@ -59,7 +59,7 @@ class TestInlineQueryResultCachedPhotoBase:
show_caption_above_media = True show_caption_above_media = True
class TestInlineQueryResultCachedPhotoWithoutRequest(TestInlineQueryResultCachedPhotoBase): class TestInlineQueryResultCachedPhotoWithoutRequest(InlineQueryResultCachedPhotoTestBase):
def test_slot_behaviour(self, inline_query_result_cached_photo): def test_slot_behaviour(self, inline_query_result_cached_photo):
inst = inline_query_result_cached_photo inst = inline_query_result_cached_photo
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -31,14 +31,14 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_cached_sticker(): def inline_query_result_cached_sticker():
return InlineQueryResultCachedSticker( return InlineQueryResultCachedSticker(
TestInlineQueryResultCachedStickerBase.id_, InlineQueryResultCachedStickerTestBase.id_,
TestInlineQueryResultCachedStickerBase.sticker_file_id, InlineQueryResultCachedStickerTestBase.sticker_file_id,
input_message_content=TestInlineQueryResultCachedStickerBase.input_message_content, input_message_content=InlineQueryResultCachedStickerTestBase.input_message_content,
reply_markup=TestInlineQueryResultCachedStickerBase.reply_markup, reply_markup=InlineQueryResultCachedStickerTestBase.reply_markup,
) )
class TestInlineQueryResultCachedStickerBase: class InlineQueryResultCachedStickerTestBase:
id_ = "id" id_ = "id"
type_ = "sticker" type_ = "sticker"
sticker_file_id = "sticker file id" sticker_file_id = "sticker file id"
@ -46,7 +46,7 @@ class TestInlineQueryResultCachedStickerBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedStickerWithoutRequest(TestInlineQueryResultCachedStickerBase): class TestInlineQueryResultCachedStickerWithoutRequest(InlineQueryResultCachedStickerTestBase):
def test_slot_behaviour(self, inline_query_result_cached_sticker): def test_slot_behaviour(self, inline_query_result_cached_sticker):
inst = inline_query_result_cached_sticker inst = inline_query_result_cached_sticker
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -32,20 +32,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_cached_video(): def inline_query_result_cached_video():
return InlineQueryResultCachedVideo( return InlineQueryResultCachedVideo(
TestInlineQueryResultCachedVideoBase.id_, InlineQueryResultCachedVideoTestBase.id_,
TestInlineQueryResultCachedVideoBase.video_file_id, InlineQueryResultCachedVideoTestBase.video_file_id,
TestInlineQueryResultCachedVideoBase.title, InlineQueryResultCachedVideoTestBase.title,
caption=TestInlineQueryResultCachedVideoBase.caption, caption=InlineQueryResultCachedVideoTestBase.caption,
parse_mode=TestInlineQueryResultCachedVideoBase.parse_mode, parse_mode=InlineQueryResultCachedVideoTestBase.parse_mode,
caption_entities=TestInlineQueryResultCachedVideoBase.caption_entities, caption_entities=InlineQueryResultCachedVideoTestBase.caption_entities,
description=TestInlineQueryResultCachedVideoBase.description, description=InlineQueryResultCachedVideoTestBase.description,
input_message_content=TestInlineQueryResultCachedVideoBase.input_message_content, input_message_content=InlineQueryResultCachedVideoTestBase.input_message_content,
reply_markup=TestInlineQueryResultCachedVideoBase.reply_markup, reply_markup=InlineQueryResultCachedVideoTestBase.reply_markup,
show_caption_above_media=TestInlineQueryResultCachedVideoBase.show_caption_above_media, show_caption_above_media=InlineQueryResultCachedVideoTestBase.show_caption_above_media,
) )
class TestInlineQueryResultCachedVideoBase: class InlineQueryResultCachedVideoTestBase:
id_ = "id" id_ = "id"
type_ = "video" type_ = "video"
video_file_id = "video file id" video_file_id = "video file id"
@ -59,7 +59,7 @@ class TestInlineQueryResultCachedVideoBase:
show_caption_above_media = True show_caption_above_media = True
class TestInlineQueryResultCachedVideoWithoutRequest(TestInlineQueryResultCachedVideoBase): class TestInlineQueryResultCachedVideoWithoutRequest(InlineQueryResultCachedVideoTestBase):
def test_slot_behaviour(self, inline_query_result_cached_video): def test_slot_behaviour(self, inline_query_result_cached_video):
inst = inline_query_result_cached_video inst = inline_query_result_cached_video
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -32,18 +32,18 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_cached_voice(): def inline_query_result_cached_voice():
return InlineQueryResultCachedVoice( return InlineQueryResultCachedVoice(
TestInlineQueryResultCachedVoiceBase.id_, InlineQueryResultCachedVoiceTestBase.id_,
TestInlineQueryResultCachedVoiceBase.voice_file_id, InlineQueryResultCachedVoiceTestBase.voice_file_id,
TestInlineQueryResultCachedVoiceBase.title, InlineQueryResultCachedVoiceTestBase.title,
caption=TestInlineQueryResultCachedVoiceBase.caption, caption=InlineQueryResultCachedVoiceTestBase.caption,
parse_mode=TestInlineQueryResultCachedVoiceBase.parse_mode, parse_mode=InlineQueryResultCachedVoiceTestBase.parse_mode,
caption_entities=TestInlineQueryResultCachedVoiceBase.caption_entities, caption_entities=InlineQueryResultCachedVoiceTestBase.caption_entities,
input_message_content=TestInlineQueryResultCachedVoiceBase.input_message_content, input_message_content=InlineQueryResultCachedVoiceTestBase.input_message_content,
reply_markup=TestInlineQueryResultCachedVoiceBase.reply_markup, reply_markup=InlineQueryResultCachedVoiceTestBase.reply_markup,
) )
class TestInlineQueryResultCachedVoiceBase: class InlineQueryResultCachedVoiceTestBase:
id_ = "id" id_ = "id"
type_ = "voice" type_ = "voice"
voice_file_id = "voice file id" voice_file_id = "voice file id"
@ -55,7 +55,7 @@ class TestInlineQueryResultCachedVoiceBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedVoiceWithoutRequest(TestInlineQueryResultCachedVoiceBase): class TestInlineQueryResultCachedVoiceWithoutRequest(InlineQueryResultCachedVoiceTestBase):
def test_slot_behaviour(self, inline_query_result_cached_voice): def test_slot_behaviour(self, inline_query_result_cached_voice):
inst = inline_query_result_cached_voice inst = inline_query_result_cached_voice
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -31,19 +31,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_contact(): def inline_query_result_contact():
return InlineQueryResultContact( return InlineQueryResultContact(
TestInlineQueryResultContactBase.id_, InlineQueryResultContactTestBase.id_,
TestInlineQueryResultContactBase.phone_number, InlineQueryResultContactTestBase.phone_number,
TestInlineQueryResultContactBase.first_name, InlineQueryResultContactTestBase.first_name,
last_name=TestInlineQueryResultContactBase.last_name, last_name=InlineQueryResultContactTestBase.last_name,
thumbnail_url=TestInlineQueryResultContactBase.thumbnail_url, thumbnail_url=InlineQueryResultContactTestBase.thumbnail_url,
thumbnail_width=TestInlineQueryResultContactBase.thumbnail_width, thumbnail_width=InlineQueryResultContactTestBase.thumbnail_width,
thumbnail_height=TestInlineQueryResultContactBase.thumbnail_height, thumbnail_height=InlineQueryResultContactTestBase.thumbnail_height,
input_message_content=TestInlineQueryResultContactBase.input_message_content, input_message_content=InlineQueryResultContactTestBase.input_message_content,
reply_markup=TestInlineQueryResultContactBase.reply_markup, reply_markup=InlineQueryResultContactTestBase.reply_markup,
) )
class TestInlineQueryResultContactBase: class InlineQueryResultContactTestBase:
id_ = "id" id_ = "id"
type_ = "contact" type_ = "contact"
phone_number = "phone_number" phone_number = "phone_number"
@ -56,7 +56,7 @@ class TestInlineQueryResultContactBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultContactWithoutRequest(TestInlineQueryResultContactBase): class TestInlineQueryResultContactWithoutRequest(InlineQueryResultContactTestBase):
def test_slot_behaviour(self, inline_query_result_contact): def test_slot_behaviour(self, inline_query_result_contact):
inst = inline_query_result_contact inst = inline_query_result_contact
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -32,23 +32,23 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_document(): def inline_query_result_document():
return InlineQueryResultDocument( return InlineQueryResultDocument(
TestInlineQueryResultDocumentBase.id_, InlineQueryResultDocumentTestBase.id_,
TestInlineQueryResultDocumentBase.document_url, InlineQueryResultDocumentTestBase.document_url,
TestInlineQueryResultDocumentBase.title, InlineQueryResultDocumentTestBase.title,
TestInlineQueryResultDocumentBase.mime_type, InlineQueryResultDocumentTestBase.mime_type,
caption=TestInlineQueryResultDocumentBase.caption, caption=InlineQueryResultDocumentTestBase.caption,
parse_mode=TestInlineQueryResultDocumentBase.parse_mode, parse_mode=InlineQueryResultDocumentTestBase.parse_mode,
caption_entities=TestInlineQueryResultDocumentBase.caption_entities, caption_entities=InlineQueryResultDocumentTestBase.caption_entities,
description=TestInlineQueryResultDocumentBase.description, description=InlineQueryResultDocumentTestBase.description,
thumbnail_url=TestInlineQueryResultDocumentBase.thumbnail_url, thumbnail_url=InlineQueryResultDocumentTestBase.thumbnail_url,
thumbnail_width=TestInlineQueryResultDocumentBase.thumbnail_width, thumbnail_width=InlineQueryResultDocumentTestBase.thumbnail_width,
thumbnail_height=TestInlineQueryResultDocumentBase.thumbnail_height, thumbnail_height=InlineQueryResultDocumentTestBase.thumbnail_height,
input_message_content=TestInlineQueryResultDocumentBase.input_message_content, input_message_content=InlineQueryResultDocumentTestBase.input_message_content,
reply_markup=TestInlineQueryResultDocumentBase.reply_markup, reply_markup=InlineQueryResultDocumentTestBase.reply_markup,
) )
class TestInlineQueryResultDocumentBase: class InlineQueryResultDocumentTestBase:
id_ = "id" id_ = "id"
type_ = "document" type_ = "document"
document_url = "document url" document_url = "document url"
@ -65,7 +65,7 @@ class TestInlineQueryResultDocumentBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultDocumentWithoutRequest(TestInlineQueryResultDocumentBase): class TestInlineQueryResultDocumentWithoutRequest(InlineQueryResultDocumentTestBase):
def test_slot_behaviour(self, inline_query_result_document): def test_slot_behaviour(self, inline_query_result_document):
inst = inline_query_result_document inst = inline_query_result_document
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -30,20 +30,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_game(): def inline_query_result_game():
return InlineQueryResultGame( return InlineQueryResultGame(
TestInlineQueryResultGameBase.id_, InlineQueryResultGameTestBase.id_,
TestInlineQueryResultGameBase.game_short_name, InlineQueryResultGameTestBase.game_short_name,
reply_markup=TestInlineQueryResultGameBase.reply_markup, reply_markup=InlineQueryResultGameTestBase.reply_markup,
) )
class TestInlineQueryResultGameBase: class InlineQueryResultGameTestBase:
id_ = "id" id_ = "id"
type_ = "game" type_ = "game"
game_short_name = "game short name" game_short_name = "game short name"
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultGameWithoutRequest(TestInlineQueryResultGameBase): class TestInlineQueryResultGameWithoutRequest(InlineQueryResultGameTestBase):
def test_slot_behaviour(self, inline_query_result_game): def test_slot_behaviour(self, inline_query_result_game):
inst = inline_query_result_game inst = inline_query_result_game
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -32,24 +32,24 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_gif(): def inline_query_result_gif():
return InlineQueryResultGif( return InlineQueryResultGif(
TestInlineQueryResultGifBase.id_, InlineQueryResultGifTestBase.id_,
TestInlineQueryResultGifBase.gif_url, InlineQueryResultGifTestBase.gif_url,
TestInlineQueryResultGifBase.thumbnail_url, InlineQueryResultGifTestBase.thumbnail_url,
gif_width=TestInlineQueryResultGifBase.gif_width, gif_width=InlineQueryResultGifTestBase.gif_width,
gif_height=TestInlineQueryResultGifBase.gif_height, gif_height=InlineQueryResultGifTestBase.gif_height,
gif_duration=TestInlineQueryResultGifBase.gif_duration, gif_duration=InlineQueryResultGifTestBase.gif_duration,
title=TestInlineQueryResultGifBase.title, title=InlineQueryResultGifTestBase.title,
caption=TestInlineQueryResultGifBase.caption, caption=InlineQueryResultGifTestBase.caption,
parse_mode=TestInlineQueryResultGifBase.parse_mode, parse_mode=InlineQueryResultGifTestBase.parse_mode,
caption_entities=TestInlineQueryResultGifBase.caption_entities, caption_entities=InlineQueryResultGifTestBase.caption_entities,
input_message_content=TestInlineQueryResultGifBase.input_message_content, input_message_content=InlineQueryResultGifTestBase.input_message_content,
reply_markup=TestInlineQueryResultGifBase.reply_markup, reply_markup=InlineQueryResultGifTestBase.reply_markup,
thumbnail_mime_type=TestInlineQueryResultGifBase.thumbnail_mime_type, thumbnail_mime_type=InlineQueryResultGifTestBase.thumbnail_mime_type,
show_caption_above_media=TestInlineQueryResultGifBase.show_caption_above_media, show_caption_above_media=InlineQueryResultGifTestBase.show_caption_above_media,
) )
class TestInlineQueryResultGifBase: class InlineQueryResultGifTestBase:
id_ = "id" id_ = "id"
type_ = "gif" type_ = "gif"
gif_url = "gif url" gif_url = "gif url"
@ -67,7 +67,7 @@ class TestInlineQueryResultGifBase:
show_caption_above_media = True show_caption_above_media = True
class TestInlineQueryResultGifWithoutRequest(TestInlineQueryResultGifBase): class TestInlineQueryResultGifWithoutRequest(InlineQueryResultGifTestBase):
def test_slot_behaviour(self, inline_query_result_gif): def test_slot_behaviour(self, inline_query_result_gif):
inst = inline_query_result_gif inst = inline_query_result_gif
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -31,23 +31,23 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_location(): def inline_query_result_location():
return InlineQueryResultLocation( return InlineQueryResultLocation(
TestInlineQueryResultLocationBase.id_, InlineQueryResultLocationTestBase.id_,
TestInlineQueryResultLocationBase.latitude, InlineQueryResultLocationTestBase.latitude,
TestInlineQueryResultLocationBase.longitude, InlineQueryResultLocationTestBase.longitude,
TestInlineQueryResultLocationBase.title, InlineQueryResultLocationTestBase.title,
live_period=TestInlineQueryResultLocationBase.live_period, live_period=InlineQueryResultLocationTestBase.live_period,
thumbnail_url=TestInlineQueryResultLocationBase.thumbnail_url, thumbnail_url=InlineQueryResultLocationTestBase.thumbnail_url,
thumbnail_width=TestInlineQueryResultLocationBase.thumbnail_width, thumbnail_width=InlineQueryResultLocationTestBase.thumbnail_width,
thumbnail_height=TestInlineQueryResultLocationBase.thumbnail_height, thumbnail_height=InlineQueryResultLocationTestBase.thumbnail_height,
input_message_content=TestInlineQueryResultLocationBase.input_message_content, input_message_content=InlineQueryResultLocationTestBase.input_message_content,
reply_markup=TestInlineQueryResultLocationBase.reply_markup, reply_markup=InlineQueryResultLocationTestBase.reply_markup,
horizontal_accuracy=TestInlineQueryResultLocationBase.horizontal_accuracy, horizontal_accuracy=InlineQueryResultLocationTestBase.horizontal_accuracy,
heading=TestInlineQueryResultLocationBase.heading, heading=InlineQueryResultLocationTestBase.heading,
proximity_alert_radius=TestInlineQueryResultLocationBase.proximity_alert_radius, proximity_alert_radius=InlineQueryResultLocationTestBase.proximity_alert_radius,
) )
class TestInlineQueryResultLocationBase: class InlineQueryResultLocationTestBase:
id_ = "id" id_ = "id"
type_ = "location" type_ = "location"
latitude = 0.0 latitude = 0.0
@ -64,7 +64,7 @@ class TestInlineQueryResultLocationBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultLocationWithoutRequest(TestInlineQueryResultLocationBase): class TestInlineQueryResultLocationWithoutRequest(InlineQueryResultLocationTestBase):
def test_slot_behaviour(self, inline_query_result_location): def test_slot_behaviour(self, inline_query_result_location):
inst = inline_query_result_location inst = inline_query_result_location
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -32,24 +32,24 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_mpeg4_gif(): def inline_query_result_mpeg4_gif():
return InlineQueryResultMpeg4Gif( return InlineQueryResultMpeg4Gif(
TestInlineQueryResultMpeg4GifBase.id_, InlineQueryResultMpeg4GifTestBase.id_,
TestInlineQueryResultMpeg4GifBase.mpeg4_url, InlineQueryResultMpeg4GifTestBase.mpeg4_url,
TestInlineQueryResultMpeg4GifBase.thumbnail_url, InlineQueryResultMpeg4GifTestBase.thumbnail_url,
mpeg4_width=TestInlineQueryResultMpeg4GifBase.mpeg4_width, mpeg4_width=InlineQueryResultMpeg4GifTestBase.mpeg4_width,
mpeg4_height=TestInlineQueryResultMpeg4GifBase.mpeg4_height, mpeg4_height=InlineQueryResultMpeg4GifTestBase.mpeg4_height,
mpeg4_duration=TestInlineQueryResultMpeg4GifBase.mpeg4_duration, mpeg4_duration=InlineQueryResultMpeg4GifTestBase.mpeg4_duration,
title=TestInlineQueryResultMpeg4GifBase.title, title=InlineQueryResultMpeg4GifTestBase.title,
caption=TestInlineQueryResultMpeg4GifBase.caption, caption=InlineQueryResultMpeg4GifTestBase.caption,
parse_mode=TestInlineQueryResultMpeg4GifBase.parse_mode, parse_mode=InlineQueryResultMpeg4GifTestBase.parse_mode,
caption_entities=TestInlineQueryResultMpeg4GifBase.caption_entities, caption_entities=InlineQueryResultMpeg4GifTestBase.caption_entities,
input_message_content=TestInlineQueryResultMpeg4GifBase.input_message_content, input_message_content=InlineQueryResultMpeg4GifTestBase.input_message_content,
reply_markup=TestInlineQueryResultMpeg4GifBase.reply_markup, reply_markup=InlineQueryResultMpeg4GifTestBase.reply_markup,
thumbnail_mime_type=TestInlineQueryResultMpeg4GifBase.thumbnail_mime_type, thumbnail_mime_type=InlineQueryResultMpeg4GifTestBase.thumbnail_mime_type,
show_caption_above_media=TestInlineQueryResultMpeg4GifBase.show_caption_above_media, show_caption_above_media=InlineQueryResultMpeg4GifTestBase.show_caption_above_media,
) )
class TestInlineQueryResultMpeg4GifBase: class InlineQueryResultMpeg4GifTestBase:
id_ = "id" id_ = "id"
type_ = "mpeg4_gif" type_ = "mpeg4_gif"
mpeg4_url = "mpeg4 url" mpeg4_url = "mpeg4 url"
@ -67,7 +67,7 @@ class TestInlineQueryResultMpeg4GifBase:
show_caption_above_media = True show_caption_above_media = True
class TestInlineQueryResultMpeg4GifWithoutRequest(TestInlineQueryResultMpeg4GifBase): class TestInlineQueryResultMpeg4GifWithoutRequest(InlineQueryResultMpeg4GifTestBase):
def test_slot_behaviour(self, inline_query_result_mpeg4_gif): def test_slot_behaviour(self, inline_query_result_mpeg4_gif):
inst = inline_query_result_mpeg4_gif inst = inline_query_result_mpeg4_gif
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -32,23 +32,23 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_photo(): def inline_query_result_photo():
return InlineQueryResultPhoto( return InlineQueryResultPhoto(
TestInlineQueryResultPhotoBase.id_, InlineQueryResultPhotoTestBase.id_,
TestInlineQueryResultPhotoBase.photo_url, InlineQueryResultPhotoTestBase.photo_url,
TestInlineQueryResultPhotoBase.thumbnail_url, InlineQueryResultPhotoTestBase.thumbnail_url,
photo_width=TestInlineQueryResultPhotoBase.photo_width, photo_width=InlineQueryResultPhotoTestBase.photo_width,
photo_height=TestInlineQueryResultPhotoBase.photo_height, photo_height=InlineQueryResultPhotoTestBase.photo_height,
title=TestInlineQueryResultPhotoBase.title, title=InlineQueryResultPhotoTestBase.title,
description=TestInlineQueryResultPhotoBase.description, description=InlineQueryResultPhotoTestBase.description,
caption=TestInlineQueryResultPhotoBase.caption, caption=InlineQueryResultPhotoTestBase.caption,
parse_mode=TestInlineQueryResultPhotoBase.parse_mode, parse_mode=InlineQueryResultPhotoTestBase.parse_mode,
caption_entities=TestInlineQueryResultPhotoBase.caption_entities, caption_entities=InlineQueryResultPhotoTestBase.caption_entities,
input_message_content=TestInlineQueryResultPhotoBase.input_message_content, input_message_content=InlineQueryResultPhotoTestBase.input_message_content,
reply_markup=TestInlineQueryResultPhotoBase.reply_markup, reply_markup=InlineQueryResultPhotoTestBase.reply_markup,
show_caption_above_media=TestInlineQueryResultPhotoBase.show_caption_above_media, show_caption_above_media=InlineQueryResultPhotoTestBase.show_caption_above_media,
) )
class TestInlineQueryResultPhotoBase: class InlineQueryResultPhotoTestBase:
id_ = "id" id_ = "id"
type_ = "photo" type_ = "photo"
photo_url = "photo url" photo_url = "photo url"
@ -66,7 +66,7 @@ class TestInlineQueryResultPhotoBase:
show_caption_above_media = True show_caption_above_media = True
class TestInlineQueryResultPhotoWithoutRequest(TestInlineQueryResultPhotoBase): class TestInlineQueryResultPhotoWithoutRequest(InlineQueryResultPhotoTestBase):
def test_slot_behaviour(self, inline_query_result_photo): def test_slot_behaviour(self, inline_query_result_photo):
inst = inline_query_result_photo inst = inline_query_result_photo
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -31,24 +31,24 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_venue(): def inline_query_result_venue():
return InlineQueryResultVenue( return InlineQueryResultVenue(
TestInlineQueryResultVenueBase.id_, InlineQueryResultVenueTestBase.id_,
TestInlineQueryResultVenueBase.latitude, InlineQueryResultVenueTestBase.latitude,
TestInlineQueryResultVenueBase.longitude, InlineQueryResultVenueTestBase.longitude,
TestInlineQueryResultVenueBase.title, InlineQueryResultVenueTestBase.title,
TestInlineQueryResultVenueBase.address, InlineQueryResultVenueTestBase.address,
foursquare_id=TestInlineQueryResultVenueBase.foursquare_id, foursquare_id=InlineQueryResultVenueTestBase.foursquare_id,
foursquare_type=TestInlineQueryResultVenueBase.foursquare_type, foursquare_type=InlineQueryResultVenueTestBase.foursquare_type,
thumbnail_url=TestInlineQueryResultVenueBase.thumbnail_url, thumbnail_url=InlineQueryResultVenueTestBase.thumbnail_url,
thumbnail_width=TestInlineQueryResultVenueBase.thumbnail_width, thumbnail_width=InlineQueryResultVenueTestBase.thumbnail_width,
thumbnail_height=TestInlineQueryResultVenueBase.thumbnail_height, thumbnail_height=InlineQueryResultVenueTestBase.thumbnail_height,
input_message_content=TestInlineQueryResultVenueBase.input_message_content, input_message_content=InlineQueryResultVenueTestBase.input_message_content,
reply_markup=TestInlineQueryResultVenueBase.reply_markup, reply_markup=InlineQueryResultVenueTestBase.reply_markup,
google_place_id=TestInlineQueryResultVenueBase.google_place_id, google_place_id=InlineQueryResultVenueTestBase.google_place_id,
google_place_type=TestInlineQueryResultVenueBase.google_place_type, google_place_type=InlineQueryResultVenueTestBase.google_place_type,
) )
class TestInlineQueryResultVenueBase: class InlineQueryResultVenueTestBase:
id_ = "id" id_ = "id"
type_ = "venue" type_ = "venue"
latitude = "latitude" latitude = "latitude"
@ -66,7 +66,7 @@ class TestInlineQueryResultVenueBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultVenueWithoutRequest(TestInlineQueryResultVenueBase): class TestInlineQueryResultVenueWithoutRequest(InlineQueryResultVenueTestBase):
def test_slot_behaviour(self, inline_query_result_venue): def test_slot_behaviour(self, inline_query_result_venue):
inst = inline_query_result_venue inst = inline_query_result_venue
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -32,25 +32,25 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_video(): def inline_query_result_video():
return InlineQueryResultVideo( return InlineQueryResultVideo(
TestInlineQueryResultVideoBase.id_, InlineQueryResultVideoTestBase.id_,
TestInlineQueryResultVideoBase.video_url, InlineQueryResultVideoTestBase.video_url,
TestInlineQueryResultVideoBase.mime_type, InlineQueryResultVideoTestBase.mime_type,
TestInlineQueryResultVideoBase.thumbnail_url, InlineQueryResultVideoTestBase.thumbnail_url,
TestInlineQueryResultVideoBase.title, InlineQueryResultVideoTestBase.title,
video_width=TestInlineQueryResultVideoBase.video_width, video_width=InlineQueryResultVideoTestBase.video_width,
video_height=TestInlineQueryResultVideoBase.video_height, video_height=InlineQueryResultVideoTestBase.video_height,
video_duration=TestInlineQueryResultVideoBase.video_duration, video_duration=InlineQueryResultVideoTestBase.video_duration,
caption=TestInlineQueryResultVideoBase.caption, caption=InlineQueryResultVideoTestBase.caption,
parse_mode=TestInlineQueryResultVideoBase.parse_mode, parse_mode=InlineQueryResultVideoTestBase.parse_mode,
caption_entities=TestInlineQueryResultVideoBase.caption_entities, caption_entities=InlineQueryResultVideoTestBase.caption_entities,
description=TestInlineQueryResultVideoBase.description, description=InlineQueryResultVideoTestBase.description,
input_message_content=TestInlineQueryResultVideoBase.input_message_content, input_message_content=InlineQueryResultVideoTestBase.input_message_content,
reply_markup=TestInlineQueryResultVideoBase.reply_markup, reply_markup=InlineQueryResultVideoTestBase.reply_markup,
show_caption_above_media=TestInlineQueryResultVideoBase.show_caption_above_media, show_caption_above_media=InlineQueryResultVideoTestBase.show_caption_above_media,
) )
class TestInlineQueryResultVideoBase: class InlineQueryResultVideoTestBase:
id_ = "id" id_ = "id"
type_ = "video" type_ = "video"
video_url = "video url" video_url = "video url"
@ -69,7 +69,7 @@ class TestInlineQueryResultVideoBase:
show_caption_above_media = True show_caption_above_media = True
class TestInlineQueryResultVideoWithoutRequest(TestInlineQueryResultVideoBase): class TestInlineQueryResultVideoWithoutRequest(InlineQueryResultVideoTestBase):
def test_slot_behaviour(self, inline_query_result_video): def test_slot_behaviour(self, inline_query_result_video):
inst = inline_query_result_video inst = inline_query_result_video
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -32,19 +32,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def inline_query_result_voice(): def inline_query_result_voice():
return InlineQueryResultVoice( return InlineQueryResultVoice(
id=TestInlineQueryResultVoiceBase.id_, id=InlineQueryResultVoiceTestBase.id_,
voice_url=TestInlineQueryResultVoiceBase.voice_url, voice_url=InlineQueryResultVoiceTestBase.voice_url,
title=TestInlineQueryResultVoiceBase.title, title=InlineQueryResultVoiceTestBase.title,
voice_duration=TestInlineQueryResultVoiceBase.voice_duration, voice_duration=InlineQueryResultVoiceTestBase.voice_duration,
caption=TestInlineQueryResultVoiceBase.caption, caption=InlineQueryResultVoiceTestBase.caption,
parse_mode=TestInlineQueryResultVoiceBase.parse_mode, parse_mode=InlineQueryResultVoiceTestBase.parse_mode,
caption_entities=TestInlineQueryResultVoiceBase.caption_entities, caption_entities=InlineQueryResultVoiceTestBase.caption_entities,
input_message_content=TestInlineQueryResultVoiceBase.input_message_content, input_message_content=InlineQueryResultVoiceTestBase.input_message_content,
reply_markup=TestInlineQueryResultVoiceBase.reply_markup, reply_markup=InlineQueryResultVoiceTestBase.reply_markup,
) )
class TestInlineQueryResultVoiceBase: class InlineQueryResultVoiceTestBase:
id_ = "id" id_ = "id"
type_ = "voice" type_ = "voice"
voice_url = "voice url" voice_url = "voice url"
@ -57,7 +57,7 @@ class TestInlineQueryResultVoiceBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultVoiceWithoutRequest(TestInlineQueryResultVoiceBase): class TestInlineQueryResultVoiceWithoutRequest(InlineQueryResultVoiceTestBase):
def test_slot_behaviour(self, inline_query_result_voice): def test_slot_behaviour(self, inline_query_result_voice):
inst = inline_query_result_voice inst = inline_query_result_voice
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -25,19 +25,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def input_contact_message_content(): def input_contact_message_content():
return InputContactMessageContent( return InputContactMessageContent(
TestInputContactMessageContentBase.phone_number, InputContactMessageContentTestBase.phone_number,
TestInputContactMessageContentBase.first_name, InputContactMessageContentTestBase.first_name,
TestInputContactMessageContentBase.last_name, InputContactMessageContentTestBase.last_name,
) )
class TestInputContactMessageContentBase: class InputContactMessageContentTestBase:
phone_number = "phone number" phone_number = "phone number"
first_name = "first name" first_name = "first name"
last_name = "last name" last_name = "last name"
class TestInputContactMessageContentWithoutRequest(TestInputContactMessageContentBase): class TestInputContactMessageContentWithoutRequest(InputContactMessageContentTestBase):
def test_slot_behaviour(self, input_contact_message_content): def test_slot_behaviour(self, input_contact_message_content):
inst = input_contact_message_content inst = input_contact_message_content
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -26,32 +26,32 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def input_invoice_message_content(): def input_invoice_message_content():
return InputInvoiceMessageContent( return InputInvoiceMessageContent(
title=TestInputInvoiceMessageContentBase.title, title=InputInvoiceMessageContentTestBase.title,
description=TestInputInvoiceMessageContentBase.description, description=InputInvoiceMessageContentTestBase.description,
payload=TestInputInvoiceMessageContentBase.payload, payload=InputInvoiceMessageContentTestBase.payload,
provider_token=TestInputInvoiceMessageContentBase.provider_token, provider_token=InputInvoiceMessageContentTestBase.provider_token,
currency=TestInputInvoiceMessageContentBase.currency, currency=InputInvoiceMessageContentTestBase.currency,
prices=TestInputInvoiceMessageContentBase.prices, prices=InputInvoiceMessageContentTestBase.prices,
max_tip_amount=TestInputInvoiceMessageContentBase.max_tip_amount, max_tip_amount=InputInvoiceMessageContentTestBase.max_tip_amount,
suggested_tip_amounts=TestInputInvoiceMessageContentBase.suggested_tip_amounts, suggested_tip_amounts=InputInvoiceMessageContentTestBase.suggested_tip_amounts,
provider_data=TestInputInvoiceMessageContentBase.provider_data, provider_data=InputInvoiceMessageContentTestBase.provider_data,
photo_url=TestInputInvoiceMessageContentBase.photo_url, photo_url=InputInvoiceMessageContentTestBase.photo_url,
photo_size=TestInputInvoiceMessageContentBase.photo_size, photo_size=InputInvoiceMessageContentTestBase.photo_size,
photo_width=TestInputInvoiceMessageContentBase.photo_width, photo_width=InputInvoiceMessageContentTestBase.photo_width,
photo_height=TestInputInvoiceMessageContentBase.photo_height, photo_height=InputInvoiceMessageContentTestBase.photo_height,
need_name=TestInputInvoiceMessageContentBase.need_name, need_name=InputInvoiceMessageContentTestBase.need_name,
need_phone_number=TestInputInvoiceMessageContentBase.need_phone_number, need_phone_number=InputInvoiceMessageContentTestBase.need_phone_number,
need_email=TestInputInvoiceMessageContentBase.need_email, need_email=InputInvoiceMessageContentTestBase.need_email,
need_shipping_address=TestInputInvoiceMessageContentBase.need_shipping_address, need_shipping_address=InputInvoiceMessageContentTestBase.need_shipping_address,
send_phone_number_to_provider=( send_phone_number_to_provider=(
TestInputInvoiceMessageContentBase.send_phone_number_to_provider InputInvoiceMessageContentTestBase.send_phone_number_to_provider
), ),
send_email_to_provider=TestInputInvoiceMessageContentBase.send_email_to_provider, send_email_to_provider=InputInvoiceMessageContentTestBase.send_email_to_provider,
is_flexible=TestInputInvoiceMessageContentBase.is_flexible, is_flexible=InputInvoiceMessageContentTestBase.is_flexible,
) )
class TestInputInvoiceMessageContentBase: class InputInvoiceMessageContentTestBase:
title = "invoice title" title = "invoice title"
description = "invoice description" description = "invoice description"
payload = "invoice payload" payload = "invoice payload"
@ -74,7 +74,7 @@ class TestInputInvoiceMessageContentBase:
is_flexible = True is_flexible = True
class TestInputInvoiceMessageContentWithoutRequest(TestInputInvoiceMessageContentBase): class TestInputInvoiceMessageContentWithoutRequest(InputInvoiceMessageContentTestBase):
def test_slot_behaviour(self, input_invoice_message_content): def test_slot_behaviour(self, input_invoice_message_content):
inst = input_invoice_message_content inst = input_invoice_message_content
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -25,16 +25,16 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def input_location_message_content(): def input_location_message_content():
return InputLocationMessageContent( return InputLocationMessageContent(
TestInputLocationMessageContentBase.latitude, InputLocationMessageContentTestBase.latitude,
TestInputLocationMessageContentBase.longitude, InputLocationMessageContentTestBase.longitude,
live_period=TestInputLocationMessageContentBase.live_period, live_period=InputLocationMessageContentTestBase.live_period,
horizontal_accuracy=TestInputLocationMessageContentBase.horizontal_accuracy, horizontal_accuracy=InputLocationMessageContentTestBase.horizontal_accuracy,
heading=TestInputLocationMessageContentBase.heading, heading=InputLocationMessageContentTestBase.heading,
proximity_alert_radius=TestInputLocationMessageContentBase.proximity_alert_radius, proximity_alert_radius=InputLocationMessageContentTestBase.proximity_alert_radius,
) )
class TestInputLocationMessageContentBase: class InputLocationMessageContentTestBase:
latitude = -23.691288 latitude = -23.691288
longitude = -46.788279 longitude = -46.788279
live_period = 80 live_period = 80
@ -43,7 +43,7 @@ class TestInputLocationMessageContentBase:
proximity_alert_radius = 999 proximity_alert_radius = 999
class TestInputLocationMessageContentWithoutRequest(TestInputLocationMessageContentBase): class TestInputLocationMessageContentWithoutRequest(InputLocationMessageContentTestBase):
def test_slot_behaviour(self, input_location_message_content): def test_slot_behaviour(self, input_location_message_content):
inst = input_location_message_content inst = input_location_message_content
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -26,14 +26,14 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def input_text_message_content(): def input_text_message_content():
return InputTextMessageContent( return InputTextMessageContent(
TestInputTextMessageContentBase.message_text, InputTextMessageContentTestBase.message_text,
parse_mode=TestInputTextMessageContentBase.parse_mode, parse_mode=InputTextMessageContentTestBase.parse_mode,
entities=TestInputTextMessageContentBase.entities, entities=InputTextMessageContentTestBase.entities,
link_preview_options=TestInputTextMessageContentBase.link_preview_options, link_preview_options=InputTextMessageContentTestBase.link_preview_options,
) )
class TestInputTextMessageContentBase: class InputTextMessageContentTestBase:
message_text = "*message text*" message_text = "*message text*"
parse_mode = ParseMode.MARKDOWN parse_mode = ParseMode.MARKDOWN
entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)]
@ -41,7 +41,7 @@ class TestInputTextMessageContentBase:
link_preview_options = LinkPreviewOptions(False, url="https://python-telegram-bot.org") link_preview_options = LinkPreviewOptions(False, url="https://python-telegram-bot.org")
class TestInputTextMessageContentWithoutRequest(TestInputTextMessageContentBase): class TestInputTextMessageContentWithoutRequest(InputTextMessageContentTestBase):
def test_slot_behaviour(self, input_text_message_content): def test_slot_behaviour(self, input_text_message_content):
inst = input_text_message_content inst = input_text_message_content
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -25,18 +25,18 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def input_venue_message_content(): def input_venue_message_content():
return InputVenueMessageContent( return InputVenueMessageContent(
TestInputVenueMessageContentBase.latitude, InputVenueMessageContentTestBase.latitude,
TestInputVenueMessageContentBase.longitude, InputVenueMessageContentTestBase.longitude,
TestInputVenueMessageContentBase.title, InputVenueMessageContentTestBase.title,
TestInputVenueMessageContentBase.address, InputVenueMessageContentTestBase.address,
foursquare_id=TestInputVenueMessageContentBase.foursquare_id, foursquare_id=InputVenueMessageContentTestBase.foursquare_id,
foursquare_type=TestInputVenueMessageContentBase.foursquare_type, foursquare_type=InputVenueMessageContentTestBase.foursquare_type,
google_place_id=TestInputVenueMessageContentBase.google_place_id, google_place_id=InputVenueMessageContentTestBase.google_place_id,
google_place_type=TestInputVenueMessageContentBase.google_place_type, google_place_type=InputVenueMessageContentTestBase.google_place_type,
) )
class TestInputVenueMessageContentBase: class InputVenueMessageContentTestBase:
latitude = 1.0 latitude = 1.0
longitude = 2.0 longitude = 2.0
title = "title" title = "title"
@ -47,7 +47,7 @@ class TestInputVenueMessageContentBase:
google_place_type = "google place type" google_place_type = "google place type"
class TestInputVenueMessageContentWithoutRequest(TestInputVenueMessageContentBase): class TestInputVenueMessageContentWithoutRequest(InputVenueMessageContentTestBase):
def test_slot_behaviour(self, input_venue_message_content): def test_slot_behaviour(self, input_venue_message_content):
inst = input_venue_message_content inst = input_venue_message_content
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -26,19 +26,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def encrypted_credentials(): def encrypted_credentials():
return EncryptedCredentials( return EncryptedCredentials(
TestEncryptedCredentialsBase.data, EncryptedCredentialsTestBase.data,
TestEncryptedCredentialsBase.hash, EncryptedCredentialsTestBase.hash,
TestEncryptedCredentialsBase.secret, EncryptedCredentialsTestBase.secret,
) )
class TestEncryptedCredentialsBase: class EncryptedCredentialsTestBase:
data = "data" data = "data"
hash = "hash" hash = "hash"
secret = "secret" secret = "secret"
class TestEncryptedCredentialsWithoutRequest(TestEncryptedCredentialsBase): class TestEncryptedCredentialsWithoutRequest(EncryptedCredentialsTestBase):
def test_slot_behaviour(self, encrypted_credentials): def test_slot_behaviour(self, encrypted_credentials):
inst = encrypted_credentials inst = encrypted_credentials
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -26,19 +26,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def encrypted_passport_element(): def encrypted_passport_element():
return EncryptedPassportElement( return EncryptedPassportElement(
TestEncryptedPassportElementBase.type_, EncryptedPassportElementTestBase.type_,
"this is a hash", "this is a hash",
data=TestEncryptedPassportElementBase.data, data=EncryptedPassportElementTestBase.data,
phone_number=TestEncryptedPassportElementBase.phone_number, phone_number=EncryptedPassportElementTestBase.phone_number,
email=TestEncryptedPassportElementBase.email, email=EncryptedPassportElementTestBase.email,
files=TestEncryptedPassportElementBase.files, files=EncryptedPassportElementTestBase.files,
front_side=TestEncryptedPassportElementBase.front_side, front_side=EncryptedPassportElementTestBase.front_side,
reverse_side=TestEncryptedPassportElementBase.reverse_side, reverse_side=EncryptedPassportElementTestBase.reverse_side,
selfie=TestEncryptedPassportElementBase.selfie, selfie=EncryptedPassportElementTestBase.selfie,
) )
class TestEncryptedPassportElementBase: class EncryptedPassportElementTestBase:
type_ = "type" type_ = "type"
hash = "this is a hash" hash = "this is a hash"
data = "data" data = "data"
@ -50,7 +50,7 @@ class TestEncryptedPassportElementBase:
selfie = PassportFile("file_id", 50, 0, 25) selfie = PassportFile("file_id", 50, 0, 25)
class TestEncryptedPassportElementWithoutRequest(TestEncryptedPassportElementBase): class TestEncryptedPassportElementWithoutRequest(EncryptedPassportElementTestBase):
def test_slot_behaviour(self, encrypted_passport_element): def test_slot_behaviour(self, encrypted_passport_element):
inst = encrypted_passport_element inst = encrypted_passport_element
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -220,7 +220,7 @@ def passport_data(bot):
return PassportData.de_json(RAW_PASSPORT_DATA, bot=bot) return PassportData.de_json(RAW_PASSPORT_DATA, bot=bot)
class TestPassportBase: class PassportTestBase:
driver_license_selfie_file_id = "DgADBAADEQQAAkopgFNr6oi-wISRtAI" driver_license_selfie_file_id = "DgADBAADEQQAAkopgFNr6oi-wISRtAI"
driver_license_selfie_file_unique_id = "d4e390cca57b4da5a65322b304762a12" driver_license_selfie_file_unique_id = "d4e390cca57b4da5a65322b304762a12"
driver_license_front_side_file_id = "DgADBAADxwMAApnQgVPK2-ckL2eXVAI" driver_license_front_side_file_id = "DgADBAADxwMAApnQgVPK2-ckL2eXVAI"
@ -243,7 +243,7 @@ class TestPassportBase:
driver_license_selfie_credentials_secret = "tivdId6RNYNsvXYPppdzrbxOBuBOr9wXRPDcCvnXU7E=" driver_license_selfie_credentials_secret = "tivdId6RNYNsvXYPppdzrbxOBuBOr9wXRPDcCvnXU7E="
class TestPassportWithoutRequest(TestPassportBase): class TestPassportWithoutRequest(PassportTestBase):
def test_slot_behaviour(self, passport_data): def test_slot_behaviour(self, passport_data):
inst = passport_data inst = passport_data
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -25,14 +25,14 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def passport_element_error_data_field(): def passport_element_error_data_field():
return PassportElementErrorDataField( return PassportElementErrorDataField(
TestPassportElementErrorDataFieldBase.type_, PassportElementErrorDataFieldTestBase.type_,
TestPassportElementErrorDataFieldBase.field_name, PassportElementErrorDataFieldTestBase.field_name,
TestPassportElementErrorDataFieldBase.data_hash, PassportElementErrorDataFieldTestBase.data_hash,
TestPassportElementErrorDataFieldBase.message, PassportElementErrorDataFieldTestBase.message,
) )
class TestPassportElementErrorDataFieldBase: class PassportElementErrorDataFieldTestBase:
source = "data" source = "data"
type_ = "test_type" type_ = "test_type"
field_name = "test_field" field_name = "test_field"
@ -40,7 +40,7 @@ class TestPassportElementErrorDataFieldBase:
message = "Error message" message = "Error message"
class TestPassportElementErrorDataFieldWithoutRequest(TestPassportElementErrorDataFieldBase): class TestPassportElementErrorDataFieldWithoutRequest(PassportElementErrorDataFieldTestBase):
def test_slot_behaviour(self, passport_element_error_data_field): def test_slot_behaviour(self, passport_element_error_data_field):
inst = passport_element_error_data_field inst = passport_element_error_data_field
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -25,20 +25,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def passport_element_error_file(): def passport_element_error_file():
return PassportElementErrorFile( return PassportElementErrorFile(
TestPassportElementErrorFileBase.type_, PassportElementErrorFileTestBase.type_,
TestPassportElementErrorFileBase.file_hash, PassportElementErrorFileTestBase.file_hash,
TestPassportElementErrorFileBase.message, PassportElementErrorFileTestBase.message,
) )
class TestPassportElementErrorFileBase: class PassportElementErrorFileTestBase:
source = "file" source = "file"
type_ = "test_type" type_ = "test_type"
file_hash = "file_hash" file_hash = "file_hash"
message = "Error message" message = "Error message"
class TestPassportElementErrorFileWithoutRequest(TestPassportElementErrorFileBase): class TestPassportElementErrorFileWithoutRequest(PassportElementErrorFileTestBase):
def test_slot_behaviour(self, passport_element_error_file): def test_slot_behaviour(self, passport_element_error_file):
inst = passport_element_error_file inst = passport_element_error_file
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -26,20 +26,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def passport_element_error_files(): def passport_element_error_files():
return PassportElementErrorFiles( return PassportElementErrorFiles(
TestPassportElementErrorFilesBase.type_, PassportElementErrorFilesTestBase.type_,
TestPassportElementErrorFilesBase.file_hashes, PassportElementErrorFilesTestBase.file_hashes,
TestPassportElementErrorFilesBase.message, PassportElementErrorFilesTestBase.message,
) )
class TestPassportElementErrorFilesBase: class PassportElementErrorFilesTestBase:
source = "files" source = "files"
type_ = "test_type" type_ = "test_type"
file_hashes = ["hash1", "hash2"] file_hashes = ["hash1", "hash2"]
message = "Error message" message = "Error message"
class TestPassportElementErrorFilesWithoutRequest(TestPassportElementErrorFilesBase): class TestPassportElementErrorFilesWithoutRequest(PassportElementErrorFilesTestBase):
def test_slot_behaviour(self, passport_element_error_files): def test_slot_behaviour(self, passport_element_error_files):
inst = passport_element_error_files inst = passport_element_error_files
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -25,20 +25,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def passport_element_error_front_side(): def passport_element_error_front_side():
return PassportElementErrorFrontSide( return PassportElementErrorFrontSide(
TestPassportElementErrorFrontSideBase.type_, PassportElementErrorFrontSideTestBase.type_,
TestPassportElementErrorFrontSideBase.file_hash, PassportElementErrorFrontSideTestBase.file_hash,
TestPassportElementErrorFrontSideBase.message, PassportElementErrorFrontSideTestBase.message,
) )
class TestPassportElementErrorFrontSideBase: class PassportElementErrorFrontSideTestBase:
source = "front_side" source = "front_side"
type_ = "test_type" type_ = "test_type"
file_hash = "file_hash" file_hash = "file_hash"
message = "Error message" message = "Error message"
class TestPassportElementErrorFrontSideWithoutRequest(TestPassportElementErrorFrontSideBase): class TestPassportElementErrorFrontSideWithoutRequest(PassportElementErrorFrontSideTestBase):
def test_slot_behaviour(self, passport_element_error_front_side): def test_slot_behaviour(self, passport_element_error_front_side):
inst = passport_element_error_front_side inst = passport_element_error_front_side
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -25,20 +25,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def passport_element_error_reverse_side(): def passport_element_error_reverse_side():
return PassportElementErrorReverseSide( return PassportElementErrorReverseSide(
TestPassportElementErrorReverseSideBase.type_, PassportElementErrorReverseSideTestBase.type_,
TestPassportElementErrorReverseSideBase.file_hash, PassportElementErrorReverseSideTestBase.file_hash,
TestPassportElementErrorReverseSideBase.message, PassportElementErrorReverseSideTestBase.message,
) )
class TestPassportElementErrorReverseSideBase: class PassportElementErrorReverseSideTestBase:
source = "reverse_side" source = "reverse_side"
type_ = "test_type" type_ = "test_type"
file_hash = "file_hash" file_hash = "file_hash"
message = "Error message" message = "Error message"
class TestPassportElementErrorReverseSideWithoutRequest(TestPassportElementErrorReverseSideBase): class TestPassportElementErrorReverseSideWithoutRequest(PassportElementErrorReverseSideTestBase):
def test_slot_behaviour(self, passport_element_error_reverse_side): def test_slot_behaviour(self, passport_element_error_reverse_side):
inst = passport_element_error_reverse_side inst = passport_element_error_reverse_side
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -25,20 +25,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def passport_element_error_selfie(): def passport_element_error_selfie():
return PassportElementErrorSelfie( return PassportElementErrorSelfie(
TestPassportElementErrorSelfieBase.type_, PassportElementErrorSelfieTestBase.type_,
TestPassportElementErrorSelfieBase.file_hash, PassportElementErrorSelfieTestBase.file_hash,
TestPassportElementErrorSelfieBase.message, PassportElementErrorSelfieTestBase.message,
) )
class TestPassportElementErrorSelfieBase: class PassportElementErrorSelfieTestBase:
source = "selfie" source = "selfie"
type_ = "test_type" type_ = "test_type"
file_hash = "file_hash" file_hash = "file_hash"
message = "Error message" message = "Error message"
class TestPassportElementErrorSelfieWithoutRequest(TestPassportElementErrorSelfieBase): class TestPassportElementErrorSelfieWithoutRequest(PassportElementErrorSelfieTestBase):
def test_slot_behaviour(self, passport_element_error_selfie): def test_slot_behaviour(self, passport_element_error_selfie):
inst = passport_element_error_selfie inst = passport_element_error_selfie
for attr in inst.__slots__: for attr in inst.__slots__:

View file

@ -25,13 +25,13 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def passport_element_error_translation_file(): def passport_element_error_translation_file():
return PassportElementErrorTranslationFile( return PassportElementErrorTranslationFile(
TestPassportElementErrorTranslationFileBase.type_, PassportElementErrorTranslationFileTestBase.type_,
TestPassportElementErrorTranslationFileBase.file_hash, PassportElementErrorTranslationFileTestBase.file_hash,
TestPassportElementErrorTranslationFileBase.message, PassportElementErrorTranslationFileTestBase.message,
) )
class TestPassportElementErrorTranslationFileBase: class PassportElementErrorTranslationFileTestBase:
source = "translation_file" source = "translation_file"
type_ = "test_type" type_ = "test_type"
file_hash = "file_hash" file_hash = "file_hash"
@ -39,7 +39,7 @@ class TestPassportElementErrorTranslationFileBase:
class TestPassportElementErrorTranslationFileWithoutRequest( class TestPassportElementErrorTranslationFileWithoutRequest(
TestPassportElementErrorTranslationFileBase PassportElementErrorTranslationFileTestBase
): ):
def test_slot_behaviour(self, passport_element_error_translation_file): def test_slot_behaviour(self, passport_element_error_translation_file):
inst = passport_element_error_translation_file inst = passport_element_error_translation_file

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