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}}
strategy:
matrix:
python-version: [3.9]
python-version: [3.10]
os: [ubuntu-latest]
fail-fast: False
steps:

View file

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

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
=========
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
============

View file

@ -11,7 +11,7 @@
:target: https://pypi.org/project/python-telegram-bot/
:alt: Supported Python versions
.. image:: https://img.shields.io/badge/Bot%20API-7.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
:alt: Supported Bot API version
@ -81,7 +81,7 @@ After installing_ the library, be sure to check out the section on `working with
Telegram API support
~~~~~~~~~~~~~~~~~~~~
All types and methods of the Telegram Bot API **7.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>`_.
Notable Features
@ -119,7 +119,7 @@ Verifying Releases
To enable you to verify that a release file that you downloaded was indeed provided by the ``python-telegram-bot`` team, we have taken the following measures.
Starting with v21.4, all releases are signed via `sigstore <https://sigstore.dev>`_.
Starting with v21.4, all releases are signed via `sigstore <https://www.sigstore.dev>`_.
The corresponding signature files are uploaded to the `GitHub releases page`_.
To verify the signature, please install the `sigstore Python client <https://pypi.org/project/sigstore/>`_ and follow the instructions for `verifying signatures from GitHub Actions <https://github.com/sigstore/sigstore-python#signatures-from-github-actions>`_. As input for the ``--repository`` parameter, please use the value ``python-telegram-bot/python-telegram-bot``.
@ -157,7 +157,7 @@ PTB can be installed with optional dependencies:
* ``pip install "python-telegram-bot[http2]"`` installs `httpx[http2] <https://www.python-httpx.org/#dependencies>`_. Use this, if you want to use HTTP/2.
* ``pip install "python-telegram-bot[rate-limiter]"`` installs `aiolimiter~=1.1.0 <https://aiolimiter.readthedocs.io/en/stable/>`_. Use this, if you want to use ``telegram.ext.AIORateLimiter``.
* ``pip install "python-telegram-bot[webhooks]"`` installs the `tornado~=6.4 <https://www.tornadoweb.org/en/stable/>`_ library. Use this, if you want to use ``telegram.ext.Updater.start_webhook``/``telegram.ext.Application.run_webhook``.
* ``pip install "python-telegram-bot[callback-data]"`` installs the `cachetools>=5.3.3,<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``.
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.reactiontypecustomemoji
telegram.reactiontypeemoji
telegram.reactiontypepaid
telegram.replykeyboardmarkup
telegram.replykeyboardremove
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.
* You can manually add multiple buttons to your game message. Please note that the first button in the first row **must always** launch the game, using the field ``callback_game`` in :class:`telegram.InlineKeyboardButton`. You can add extra buttons according to taste: e.g., for a description of the rules, or to open the game's official community.
* To make your game more attractive, you can upload a GIF animation that demonstrates the game to the users via `BotFather <https://t.me/botfather>`_ (see `Lumberjack <https://t.me/gamebot?game=lumberjack>`_ for example).
* A game message will also display high scores for the current chat. Use :meth:`~telegram.Bot.setGameScore` to post high scores to the chat with the game, 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.
* 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.

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_url_note| replace:: Removed the deprecated argument and attribute ``thumb_url``.
.. |removed_thumb_url_note| replace:: Removed the deprecated argument and attribute ``thumb_url`` which made thumbnail_url mandatory.
.. |removed_thumb_wildcard_note| replace:: Removed the deprecated arguments and attributes ``thumb_*``.
.. |thumbnail_url_mandatory| replace:: Removal of the deprecated argument ``thumb_url`` made ``thumbnail_url`` mandatory.
.. |async_context_manager| replace:: Asynchronous context manager which
.. |reply_parameters| replace:: Description of the message to reply to.

View file

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

View file

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

View file

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

View file

@ -2666,6 +2666,81 @@ class _ChatBase(TelegramObject):
api_kwargs=api_kwargs,
)
async def create_subscription_invite_link(
self,
subscription_period: int,
subscription_price: int,
name: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> "ChatInviteLink":
"""Shortcut for::
await bot.create_chat_subscription_invite_link(
chat_id=update.effective_chat.id, *args, **kwargs
)
For the documentation of the arguments, please see
:meth:`telegram.Bot.create_chat_subscription_invite_link`.
.. versionadded:: 21.5
Returns:
:class:`telegram.ChatInviteLink`
"""
return await self.get_bot().create_chat_subscription_invite_link(
chat_id=self.id,
subscription_period=subscription_period,
subscription_price=subscription_price,
name=name,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
)
async def edit_subscription_invite_link(
self,
invite_link: Union[str, "ChatInviteLink"],
name: Optional[str] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: Optional[JSONDict] = None,
) -> "ChatInviteLink":
"""Shortcut for::
await bot.edit_chat_subscription_invite_link(
chat_id=update.effective_chat.id, *args, **kwargs
)
For the documentation of the arguments, please see
:meth:`telegram.Bot.edit_chat_subscription_invite_link`.
.. versionadded:: 21.5
Returns:
:class:`telegram.ChatInviteLink`
"""
return await self.get_bot().edit_chat_subscription_invite_link(
chat_id=self.id,
invite_link=invite_link,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
name=name,
)
async def approve_join_request(
self,
user_id: int,
@ -3274,6 +3349,7 @@ class _ChatBase(TelegramObject):
protect_content: ODVInput[bool] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
reply_markup: Optional[ReplyMarkup] = None,
business_connection_id: Optional[str] = None,
*,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
@ -3314,6 +3390,7 @@ class _ChatBase(TelegramObject):
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
business_connection_id=business_connection_id,
)

View file

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

View file

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

View file

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

View file

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

View file

@ -78,7 +78,7 @@ class InputFile:
# here the file handle is already closed and the upload will fail
await bot.send_document(chat_id, input_file)
.. versionadded:: NEXT.VERSION
.. versionadded:: 21.5
Attributes:
@ -126,7 +126,7 @@ class InputFile:
def field_tuple(self) -> FieldTuple:
"""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.
Returns:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

@ -20,7 +20,7 @@
import copy
import itertools
from typing import TYPE_CHECKING, Dict, Final, List, Optional, Sequence
from typing import TYPE_CHECKING, Dict, Final, List, Optional, Sequence, Tuple, Union
from telegram import constants
from telegram._telegramobject import TelegramObject
@ -32,6 +32,8 @@ from telegram._utils.types import JSONDict
if TYPE_CHECKING:
from telegram import Bot
_SEM = Sequence["MessageEntity"]
class MessageEntity(TelegramObject):
"""
@ -146,9 +148,7 @@ class MessageEntity(TelegramObject):
return super().de_json(data=data, bot=bot)
@staticmethod
def adjust_message_entities_to_utf_16(
text: str, entities: Sequence["MessageEntity"]
) -> Sequence["MessageEntity"]:
def adjust_message_entities_to_utf_16(text: str, entities: _SEM) -> _SEM:
"""Utility functionality for converting the offset and length of entities from
Unicode (:obj:`str`) to UTF-16 (``utf-16-le`` encoded :obj:`bytes`).
@ -206,7 +206,7 @@ class MessageEntity(TelegramObject):
text_slice = text[last_position:position]
accumulated_length += len(text_slice.encode(TextEncoding.UTF_16_LE)) // 2
position_translation[position] = accumulated_length
# get the final output entites
# get the final output entities
out = []
for entity in entities:
translated_positions = position_translation[entity.offset]
@ -220,6 +220,143 @@ class MessageEntity(TelegramObject):
out.append(new_entity)
return out
@staticmethod
def shift_entities(by: Union[str, int], entities: _SEM) -> _SEM:
"""Utility functionality for shifting the offset of entities by a given amount.
Examples:
Shifting by an integer amount:
.. code-block:: python
text = "Hello, world!"
entities = [
MessageEntity(offset=0, length=5, type=MessageEntity.BOLD),
MessageEntity(offset=7, length=5, type=MessageEntity.ITALIC),
]
shifted_entities = MessageEntity.shift_entities(1, entities)
await bot.send_message(
chat_id=123,
text="!" + text,
entities=shifted_entities,
)
Shifting using a string:
.. code-block:: python
text = "Hello, world!"
prefix = "𝄢"
entities = [
MessageEntity(offset=0, length=5, type=MessageEntity.BOLD),
MessageEntity(offset=7, length=5, type=MessageEntity.ITALIC),
]
shifted_entities = MessageEntity.shift_entities(prefix, entities)
await bot.send_message(
chat_id=123,
text=prefix + text,
entities=shifted_entities,
)
Tip:
The :paramref:`entities` are *not* modified in place. The function returns a sequence
of new objects.
.. versionadded:: 21.5
Args:
by (:obj:`str` | :obj:`int`): Either the amount to shift the offset by or
a string whose length will be used as the amount to shift the offset by. In this
case, UTF-16 encoding will be used to calculate the length.
entities (Sequence[:class:`telegram.MessageEntity`]): Sequence of entities
Returns:
Sequence[:class:`telegram.MessageEntity`]: Sequence of entities with the offset shifted
"""
effective_shift = by if isinstance(by, int) else len(by.encode("utf-16-le")) // 2
out = []
for entity in entities:
new_entity = copy.copy(entity)
with new_entity._unfrozen():
new_entity.offset += effective_shift
out.append(new_entity)
return out
@classmethod
def concatenate(
cls,
*args: Union[Tuple[str, _SEM], Tuple[str, _SEM, bool]],
) -> Tuple[str, _SEM]:
"""Utility functionality for concatenating two text along with their formatting entities.
Tip:
This function is useful for prefixing an already formatted text with a new text and its
formatting entities. In particular, it automatically correctly handles UTF-16 encoding.
Examples:
This example shows a callback function that can be used to add a prefix and suffix to
the message in a :class:`~telegram.ext.CallbackQueryHandler`:
.. code-block:: python
async def prefix_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
prefix = "𠌕 bold 𝄢 italic underlined: 𝛙𝌢𑁍 | "
prefix_entities = [
MessageEntity(offset=2, length=4, type=MessageEntity.BOLD),
MessageEntity(offset=9, length=6, type=MessageEntity.ITALIC),
MessageEntity(offset=28, length=3, type=MessageEntity.UNDERLINE),
]
suffix = " | 𠌕 bold 𝄢 italic underlined: 𝛙𝌢𑁍"
suffix_entities = [
MessageEntity(offset=5, length=4, type=MessageEntity.BOLD),
MessageEntity(offset=12, length=6, type=MessageEntity.ITALIC),
MessageEntity(offset=31, length=3, type=MessageEntity.UNDERLINE),
]
message = update.effective_message
first = (prefix, prefix_entities, True)
second = (message.text, message.entities)
third = (suffix, suffix_entities, True)
new_text, new_entities = MessageEntity.concatenate(first, second, third)
await update.callback_query.edit_message_text(
text=new_text,
entities=new_entities,
)
Hint:
The entities are *not* modified in place. The function returns a
new sequence of objects.
.. versionadded:: 21.5
Args:
*args (Tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`]] | \
Tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`], :obj:`bool`]):
Arbitrary number of tuples containing the text and its entities to concatenate.
If the last element of the tuple is a :obj:`bool`, it is used to determine whether
to adjust the entities to UTF-16 via
:meth:`adjust_message_entities_to_utf_16`. UTF-16 adjustment is disabled by
default.
Returns:
Tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`]]: The concatenated text
and its entities
"""
output_text = ""
output_entities: List[MessageEntity] = []
for arg in args:
text, entities = arg[0], arg[1]
if len(arg) > 2 and arg[2] is True:
entities = cls.adjust_message_entities_to_utf_16(text, entities)
output_entities.extend(cls.shift_entities(output_text, entities))
output_text += text
return output_text, output_entities
ALL_TYPES: Final[List[str]] = list(constants.MessageEntityType)
"""List[:obj:`str`]: A list of all available message entity types."""
BLOCKQUOTE: Final[str] = constants.MessageEntityType.BLOCKQUOTE

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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.
Returned only in :meth:`telegram.Bot.get_me`.
.. versionadded:: NEXT.VERSION
.. versionadded:: 21.5
Attributes:
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.
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`
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):
"""This enum contains encoding schemes for text.
.. versionadded:: NEXT.VERSION
.. versionadded:: 21.5
"""
__slots__ = ()

View file

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

View file

@ -54,6 +54,7 @@ __all__ = [
"ChatLimit",
"ChatMemberStatus",
"ChatPhotoSize",
"ChatSubscriptionLimit",
"ChatType",
"ContactLimit",
"CustomEmojiStickerLimit",
@ -151,7 +152,7 @@ class _AccentColor(NamedTuple):
#: :data:`telegram.__bot_api_version_info__`.
#:
#: .. versionadded:: 20.0
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=8)
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=9)
#: :obj:`str`: Telegram Bot API
#: version supported by this version of `python-telegram-bot`. Also available as
#: :data:`telegram.__bot_api_version__`.
@ -2903,6 +2904,11 @@ class ReactionType(StringEnum):
""":obj:`str`: A :class:`telegram.ReactionType` with a normal emoji."""
CUSTOM_EMOJI = "custom_emoji"
""":obj:`str`: A :class:`telegram.ReactionType` with a custom emoji."""
PAID = "paid"
""":obj:`str`: A :class:`telegram.ReactionType` with a paid reaction.
.. versionadded:: 21.5
"""
class ReactionEmoji(StringEnum):
@ -3096,3 +3102,22 @@ class BackgroundFillType(StringEnum):
""":obj:`str`: A :class:`telegram.BackgroundFill` with gradient fill."""
FREEFORM_GRADIENT = "freeform_gradient"
""":obj:`str`: A :class:`telegram.BackgroundFill` with freeform_gradient fill."""
class ChatSubscriptionLimit(IntEnum):
"""This enum contains limitations for
:paramref:`telegram.Bot.create_chat_subscription_invite_link.subscription_period` and
:paramref:`telegram.Bot.create_chat_subscription_invite_link.subscription_price`.
The enum members of this enumeration are instances of :class:`int` and can be treated as such.
.. versionadded:: 21.5
"""
__slots__ = ()
SUBSCRIPTION_PERIOD = 2592000
""":obj:`int`: The number of seconds the subscription will be active."""
MIN_PRICE = 1
""":obj:`int`: Amount of stars a user pays, minimum amount the subscription can be set to."""
MAX_PRICE = 2500
""":obj:`int`: Amount of stars a user pays, maximum amount the subscription can be set to."""

View file

@ -4234,6 +4234,7 @@ class ExtBot(Bot, Generic[RLARGS]):
protect_content: ODVInput[bool] = DEFAULT_NONE,
reply_parameters: Optional["ReplyParameters"] = None,
reply_markup: Optional[ReplyMarkup] = None,
business_connection_id: Optional[str] = None,
*,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
@ -4263,6 +4264,57 @@ class ExtBot(Bot, Generic[RLARGS]):
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
business_connection_id=business_connection_id,
)
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
@ -4388,4 +4440,6 @@ class ExtBot(Bot, Generic[RLARGS]):
replaceStickerInSet = replace_sticker_in_set
refundStarPayment = refund_star_payment
getStarTransactions = get_star_transactions
createChatSubscriptionInviteLink = create_chat_subscription_invite_link
editChatSubscriptionInviteLink = edit_chat_subscription_invite_link
sendPaidMedia = send_paid_media

View file

@ -58,7 +58,7 @@ class CallbackQueryHandler(BaseHandler[Update, CCT]):
`~telegram.CallbackQuery.game_short_name` or :attr:`~telegram.CallbackQuery.data`
matching the defined pattern will be handled
.. versionadded:: NEXT.VERSION
.. versionadded:: 21.5
Warning:
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
handled by this handler.
.. versionadded:: NEXT.VERSION
.. versionadded:: 21.5
block (:obj:`bool`, optional): Determines whether the return value of the callback should
be awaited before processing the next handler in
:meth:`telegram.ext.Application.process_update`. Defaults to :obj:`True`.

View file

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

View file

@ -302,7 +302,7 @@ class BaseFilter:
class MessageFilter(BaseFilter):
"""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.

View file

@ -131,7 +131,7 @@ class RequestData:
def multipart_data(self) -> UploadFileDict:
"""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.
"""
multipart_data: UploadFileDict = {}

View file

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

View file

@ -51,7 +51,7 @@ async def animation(bot, chat_id):
).animation
class TestAnimationBase:
class AnimationTestBase:
animation_file_id = "CgADAQADngIAAuyVeEez0xRovKi9VAI"
animation_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
width = 320
@ -66,7 +66,7 @@ class TestAnimationBase:
caption = "Test *animation*"
class TestAnimationWithoutRequest(TestAnimationBase):
class TestAnimationWithoutRequest(AnimationTestBase):
def test_slot_behaviour(self, animation):
for attr in animation.__slots__:
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):
message = await bot.send_animation(
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
class TestAudioBase:
class AudioTestBase:
caption = "Test *audio*"
performer = "Leandro Toledo"
title = "Teste"
@ -67,7 +67,7 @@ class TestAudioBase:
audio_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestAudioWithoutRequest(TestAudioBase):
class TestAudioWithoutRequest(AudioTestBase):
def test_slot_behaviour(self, audio):
for attr in audio.__slots__:
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))
class TestAudioWithRequest(TestAudioBase):
class TestAudioWithRequest(AudioTestBase):
async def test_send_all_args(self, bot, chat_id, audio_file, thumb_file):
message = await bot.send_audio(
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_big_file_id = "bigCgADAQADngIAAuyVeEez0xRovKi9VAI"
chatphoto_small_file_unique_id = "smalladc3145fd2e84d95b64d68eaa22aa33e"
@ -60,7 +60,7 @@ class TestChatPhotoBase:
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):
for attr in chat_photo.__slots__:
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")
def contact():
return Contact(
TestContactBase.phone_number,
TestContactBase.first_name,
TestContactBase.last_name,
TestContactBase.user_id,
ContactTestBase.phone_number,
ContactTestBase.first_name,
ContactTestBase.last_name,
ContactTestBase.user_id,
)
class TestContactBase:
class ContactTestBase:
phone_number = "+11234567890"
first_name = "Leandro"
last_name = "Toledo"
user_id = 23
class TestContactWithoutRequest(TestContactBase):
class TestContactWithoutRequest(ContactTestBase):
def test_slot_behaviour(self, contact):
for attr in contact.__slots__:
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(
("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
class TestDocumentBase:
class DocumentTestBase:
caption = "DocumentTest - *Caption*"
document_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.gif"
file_size = 12948
@ -62,7 +62,7 @@ class TestDocumentBase:
document_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestDocumentWithoutRequest(TestDocumentBase):
class TestDocumentWithoutRequest(DocumentTestBase):
def test_slot_behaviour(self, document):
for attr in document.__slots__:
assert getattr(document, attr, "err") != "err", f"got extra slot '{attr}'"
@ -218,7 +218,7 @@ class TestDocumentWithoutRequest(TestDocumentBase):
assert await document.get_file()
class TestDocumentWithRequest(TestDocumentBase):
class TestDocumentWithRequest(DocumentTestBase):
async def test_error_send_empty_file(self, bot, chat_id):
with Path(os.devnull).open("rb") as f, pytest.raises(TelegramError):
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")
def file(bot):
file = File(
TestFileBase.file_id,
TestFileBase.file_unique_id,
file_path=TestFileBase.file_path,
file_size=TestFileBase.file_size,
FileTestBase.file_id,
FileTestBase.file_unique_id,
file_path=FileTestBase.file_path,
file_size=FileTestBase.file_size,
)
file.set_bot(bot)
file._unfreeze()
@ -51,10 +51,10 @@ def encrypted_file(bot):
"Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=",
)
ef = File(
TestFileBase.file_id,
TestFileBase.file_unique_id,
TestFileBase.file_size,
TestFileBase.file_path,
FileTestBase.file_id,
FileTestBase.file_unique_id,
FileTestBase.file_size,
FileTestBase.file_path,
)
ef.set_bot(bot)
ef.set_credentials(fc)
@ -69,9 +69,9 @@ def encrypted_local_file(bot):
"Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=",
)
ef = File(
TestFileBase.file_id,
TestFileBase.file_unique_id,
TestFileBase.file_size,
FileTestBase.file_id,
FileTestBase.file_unique_id,
FileTestBase.file_size,
file_path=str(data_file("image_encrypted.jpg")),
)
ef.set_bot(bot)
@ -82,16 +82,16 @@ def encrypted_local_file(bot):
@pytest.fixture(scope="module")
def local_file(bot):
file = File(
TestFileBase.file_id,
TestFileBase.file_unique_id,
FileTestBase.file_id,
FileTestBase.file_unique_id,
file_path=str(data_file("local_file.txt")),
file_size=TestFileBase.file_size,
file_size=FileTestBase.file_size,
)
file.set_bot(bot)
return file
class TestFileBase:
class FileTestBase:
file_id = "NOTVALIDDOESNOTMATTER"
file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
file_path = (
@ -101,7 +101,7 @@ class TestFileBase:
file_content = "Saint-Saëns".encode() # Intentionally contains unicode chars.
class TestFileWithoutRequest(TestFileBase):
class TestFileWithoutRequest(FileTestBase):
def test_slot_behaviour(self, file):
for attr in file.__slots__:
assert getattr(file, attr, "err") != "err", f"got extra slot '{attr}'"
@ -273,7 +273,7 @@ class TestFileWithoutRequest(TestFileBase):
assert buf2[: len(buf)] == buf
class TestFileWithRequest(TestFileBase):
class TestFileWithRequest(FileTestBase):
async def test_error_get_empty_file_id(self, bot):
with pytest.raises(TelegramError):
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")
def input_media_video(class_thumb_file):
return InputMediaVideo(
media=TestInputMediaVideoBase.media,
caption=TestInputMediaVideoBase.caption,
width=TestInputMediaVideoBase.width,
height=TestInputMediaVideoBase.height,
duration=TestInputMediaVideoBase.duration,
parse_mode=TestInputMediaVideoBase.parse_mode,
caption_entities=TestInputMediaVideoBase.caption_entities,
media=InputMediaVideoTestBase.media,
caption=InputMediaVideoTestBase.caption,
width=InputMediaVideoTestBase.width,
height=InputMediaVideoTestBase.height,
duration=InputMediaVideoTestBase.duration,
parse_mode=InputMediaVideoTestBase.parse_mode,
caption_entities=InputMediaVideoTestBase.caption_entities,
thumbnail=class_thumb_file,
supports_streaming=TestInputMediaVideoBase.supports_streaming,
has_spoiler=TestInputMediaVideoBase.has_spoiler,
show_caption_above_media=TestInputMediaVideoBase.show_caption_above_media,
supports_streaming=InputMediaVideoTestBase.supports_streaming,
has_spoiler=InputMediaVideoTestBase.has_spoiler,
show_caption_above_media=InputMediaVideoTestBase.show_caption_above_media,
)
@pytest.fixture(scope="module")
def input_media_photo():
return InputMediaPhoto(
media=TestInputMediaPhotoBase.media,
caption=TestInputMediaPhotoBase.caption,
parse_mode=TestInputMediaPhotoBase.parse_mode,
caption_entities=TestInputMediaPhotoBase.caption_entities,
has_spoiler=TestInputMediaPhotoBase.has_spoiler,
show_caption_above_media=TestInputMediaPhotoBase.show_caption_above_media,
media=InputMediaPhotoTestBase.media,
caption=InputMediaPhotoTestBase.caption,
parse_mode=InputMediaPhotoTestBase.parse_mode,
caption_entities=InputMediaPhotoTestBase.caption_entities,
has_spoiler=InputMediaPhotoTestBase.has_spoiler,
show_caption_above_media=InputMediaPhotoTestBase.show_caption_above_media,
)
@pytest.fixture(scope="module")
def input_media_animation(class_thumb_file):
return InputMediaAnimation(
media=TestInputMediaAnimationBase.media,
caption=TestInputMediaAnimationBase.caption,
parse_mode=TestInputMediaAnimationBase.parse_mode,
caption_entities=TestInputMediaAnimationBase.caption_entities,
width=TestInputMediaAnimationBase.width,
height=TestInputMediaAnimationBase.height,
media=InputMediaAnimationTestBase.media,
caption=InputMediaAnimationTestBase.caption,
parse_mode=InputMediaAnimationTestBase.parse_mode,
caption_entities=InputMediaAnimationTestBase.caption_entities,
width=InputMediaAnimationTestBase.width,
height=InputMediaAnimationTestBase.height,
thumbnail=class_thumb_file,
duration=TestInputMediaAnimationBase.duration,
has_spoiler=TestInputMediaAnimationBase.has_spoiler,
show_caption_above_media=TestInputMediaAnimationBase.show_caption_above_media,
duration=InputMediaAnimationTestBase.duration,
has_spoiler=InputMediaAnimationTestBase.has_spoiler,
show_caption_above_media=InputMediaAnimationTestBase.show_caption_above_media,
)
@pytest.fixture(scope="module")
def input_media_audio(class_thumb_file):
return InputMediaAudio(
media=TestInputMediaAudioBase.media,
caption=TestInputMediaAudioBase.caption,
duration=TestInputMediaAudioBase.duration,
performer=TestInputMediaAudioBase.performer,
title=TestInputMediaAudioBase.title,
media=InputMediaAudioTestBase.media,
caption=InputMediaAudioTestBase.caption,
duration=InputMediaAudioTestBase.duration,
performer=InputMediaAudioTestBase.performer,
title=InputMediaAudioTestBase.title,
thumbnail=class_thumb_file,
parse_mode=TestInputMediaAudioBase.parse_mode,
caption_entities=TestInputMediaAudioBase.caption_entities,
parse_mode=InputMediaAudioTestBase.parse_mode,
caption_entities=InputMediaAudioTestBase.caption_entities,
)
@pytest.fixture(scope="module")
def input_media_document(class_thumb_file):
return InputMediaDocument(
media=TestInputMediaDocumentBase.media,
caption=TestInputMediaDocumentBase.caption,
media=InputMediaDocumentTestBase.media,
caption=InputMediaDocumentTestBase.caption,
thumbnail=class_thumb_file,
parse_mode=TestInputMediaDocumentBase.parse_mode,
caption_entities=TestInputMediaDocumentBase.caption_entities,
disable_content_type_detection=TestInputMediaDocumentBase.disable_content_type_detection,
parse_mode=InputMediaDocumentTestBase.parse_mode,
caption_entities=InputMediaDocumentTestBase.caption_entities,
disable_content_type_detection=InputMediaDocumentTestBase.disable_content_type_detection,
)
@pytest.fixture(scope="module")
def input_paid_media_photo():
return InputPaidMediaPhoto(
media=TestInputMediaPhotoBase.media,
media=InputMediaPhotoTestBase.media,
)
@pytest.fixture(scope="module")
def input_paid_media_video(class_thumb_file):
return InputPaidMediaVideo(
media=TestInputMediaVideoBase.media,
media=InputMediaVideoTestBase.media,
thumbnail=class_thumb_file,
width=TestInputMediaVideoBase.width,
height=TestInputMediaVideoBase.height,
duration=TestInputMediaVideoBase.duration,
supports_streaming=TestInputMediaVideoBase.supports_streaming,
width=InputMediaVideoTestBase.width,
height=InputMediaVideoTestBase.height,
duration=InputMediaVideoTestBase.duration,
supports_streaming=InputMediaVideoTestBase.supports_streaming,
)
class TestInputMediaVideoBase:
class InputMediaVideoTestBase:
type_ = "video"
media = "NOTAREALFILEID"
caption = "My Caption"
@ -169,7 +169,7 @@ class TestInputMediaVideoBase:
show_caption_above_media = True
class TestInputMediaVideoWithoutRequest(TestInputMediaVideoBase):
class TestInputMediaVideoWithoutRequest(InputMediaVideoTestBase):
def test_slot_behaviour(self, input_media_video):
inst = input_media_video
for attr in inst.__slots__:
@ -258,7 +258,7 @@ class TestInputMediaVideoWithoutRequest(TestInputMediaVideoBase):
)
class TestInputMediaPhotoBase:
class InputMediaPhotoTestBase:
type_ = "photo"
media = "NOTAREALFILEID"
caption = "My Caption"
@ -268,7 +268,7 @@ class TestInputMediaPhotoBase:
show_caption_above_media = True
class TestInputMediaPhotoWithoutRequest(TestInputMediaPhotoBase):
class TestInputMediaPhotoWithoutRequest(InputMediaPhotoTestBase):
def test_slot_behaviour(self, input_media_photo):
inst = input_media_photo
for attr in inst.__slots__:
@ -322,7 +322,7 @@ class TestInputMediaPhotoWithoutRequest(TestInputMediaPhotoBase):
assert input_media_photo.media == data_file("telegram.mp4").as_uri()
class TestInputMediaAnimationBase:
class InputMediaAnimationTestBase:
type_ = "animation"
media = "NOTAREALFILEID"
caption = "My Caption"
@ -335,7 +335,7 @@ class TestInputMediaAnimationBase:
show_caption_above_media = True
class TestInputMediaAnimationWithoutRequest(TestInputMediaAnimationBase):
class TestInputMediaAnimationWithoutRequest(InputMediaAnimationTestBase):
def test_slot_behaviour(self, input_media_animation):
inst = input_media_animation
for attr in inst.__slots__:
@ -396,7 +396,7 @@ class TestInputMediaAnimationWithoutRequest(TestInputMediaAnimationBase):
assert input_media_animation.thumbnail == data_file("telegram.jpg").as_uri()
class TestInputMediaAudioBase:
class InputMediaAudioTestBase:
type_ = "audio"
media = "NOTAREALFILEID"
caption = "My Caption"
@ -407,7 +407,7 @@ class TestInputMediaAudioBase:
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
class TestInputMediaAudioWithoutRequest(TestInputMediaAudioBase):
class TestInputMediaAudioWithoutRequest(InputMediaAudioTestBase):
def test_slot_behaviour(self, input_media_audio):
inst = input_media_audio
for attr in inst.__slots__:
@ -467,7 +467,7 @@ class TestInputMediaAudioWithoutRequest(TestInputMediaAudioBase):
assert input_media_audio.thumbnail == data_file("telegram.jpg").as_uri()
class TestInputMediaDocumentBase:
class InputMediaDocumentTestBase:
type_ = "document"
media = "NOTAREALFILEID"
caption = "My Caption"
@ -476,7 +476,7 @@ class TestInputMediaDocumentBase:
disable_content_type_detection = True
class TestInputMediaDocumentWithoutRequest(TestInputMediaDocumentBase):
class TestInputMediaDocumentWithoutRequest(InputMediaDocumentTestBase):
def test_slot_behaviour(self, input_media_document):
inst = input_media_document
for attr in inst.__slots__:
@ -535,7 +535,7 @@ class TestInputMediaDocumentWithoutRequest(TestInputMediaDocumentBase):
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):
inst = input_paid_media_photo
for attr in inst.__slots__:
@ -568,7 +568,7 @@ class TestInputPaidMediaPhotoWithoutRequest(TestInputMediaPhotoBase):
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):
inst = input_paid_media_video
for attr in inst.__slots__:

View file

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

View file

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

View file

@ -65,7 +65,7 @@ def photo(photolist):
return photolist[-1]
class TestPhotoBase:
class PhotoTestBase:
width = 800
height = 800
caption = "<b>PhotoTest</b> - *Caption*"
@ -75,7 +75,7 @@ class TestPhotoBase:
file_size = [29176, 27662]
class TestPhotoWithoutRequest(TestPhotoBase):
class TestPhotoWithoutRequest(PhotoTestBase):
def test_slot_behaviour(self, photo):
for attr in photo.__slots__:
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))
class TestPhotoWithRequest(TestPhotoBase):
class TestPhotoWithRequest(PhotoTestBase):
async def test_send_photo_all_args(self, bot, chat_id, photo_file):
message = await bot.send_photo(
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
# necessary to properly test needs_repainting
with sticker._unfrozen():
sticker.needs_repainting = TestStickerBase.needs_repainting
sticker.needs_repainting = StickerTestBase.needs_repainting
return sticker
@ -89,7 +89,7 @@ def video_sticker(bot, chat_id):
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'
# Serving sticker from gh since our server sends wrong content_type
sticker_file_url = (
@ -116,7 +116,7 @@ class TestStickerBase:
premium_animation = File("this_is_an_id", "this_is_an_unique_id")
class TestStickerWithoutRequest(TestStickerBase):
class TestStickerWithoutRequest(StickerTestBase):
def test_slot_behaviour(self, sticker):
for attr in sticker.__slots__:
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):
message = await bot.send_sticker(
chat_id, sticker=sticker_file, disable_notification=False, protect_content=True
@ -572,7 +572,7 @@ def sticker_set_thumb_file():
yield file
class TestStickerSetBase:
class StickerSetTestBase:
title = "Test stickers"
stickers = [Sticker("file_id", "file_un_id", 512, 512, True, True, Sticker.REGULAR)]
name = "NOTAREALNAME"
@ -580,7 +580,7 @@ class TestStickerSetBase:
contains_masks = True
class TestStickerSetWithoutRequest(TestStickerSetBase):
class TestStickerSetWithoutRequest(StickerSetTestBase):
def test_slot_behaviour(self):
inst = StickerSet("this", "is", self.stickers, "not")
for attr in inst.__slots__:
@ -1064,21 +1064,21 @@ class TestStickerSetWithRequest:
@pytest.fixture(scope="module")
def mask_position():
return MaskPosition(
TestMaskPositionBase.point,
TestMaskPositionBase.x_shift,
TestMaskPositionBase.y_shift,
TestMaskPositionBase.scale,
MaskPositionTestBase.point,
MaskPositionTestBase.x_shift,
MaskPositionTestBase.y_shift,
MaskPositionTestBase.scale,
)
class TestMaskPositionBase:
class MaskPositionTestBase:
point = MaskPosition.EYES
x_shift = -1
y_shift = 1
scale = 2
class TestMaskPositionWithoutRequest(TestMaskPositionBase):
class TestMaskPositionWithoutRequest(MaskPositionTestBase):
def test_slot_behaviour(self, mask_position):
inst = mask_position
for attr in inst.__slots__:
@ -1130,7 +1130,7 @@ class TestMaskPositionWithoutRequest(TestMaskPositionBase):
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):
name = f"masks_by_{bot.username}"
try:

View file

@ -31,17 +31,17 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def venue():
return Venue(
TestVenueBase.location,
TestVenueBase.title,
TestVenueBase.address,
foursquare_id=TestVenueBase.foursquare_id,
foursquare_type=TestVenueBase.foursquare_type,
google_place_id=TestVenueBase.google_place_id,
google_place_type=TestVenueBase.google_place_type,
VenueTestBase.location,
VenueTestBase.title,
VenueTestBase.address,
foursquare_id=VenueTestBase.foursquare_id,
foursquare_type=VenueTestBase.foursquare_type,
google_place_id=VenueTestBase.google_place_id,
google_place_type=VenueTestBase.google_place_type,
)
class TestVenueBase:
class VenueTestBase:
location = Location(longitude=-46.788279, latitude=-23.691288)
title = "title"
address = "address"
@ -51,7 +51,7 @@ class TestVenueBase:
google_place_type = "google place type"
class TestVenueWithoutRequest(TestVenueBase):
class TestVenueWithoutRequest(VenueTestBase):
def test_slot_behaviour(self, venue):
for attr in venue.__slots__:
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(
("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
class TestVideoBase:
class VideoTestBase:
width = 360
height = 640
duration = 5
@ -66,7 +66,7 @@ class TestVideoBase:
video_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestVideoWithoutRequest(TestVideoBase):
class TestVideoWithoutRequest(VideoTestBase):
def test_slot_behaviour(self, video):
for attr in video.__slots__:
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))
class TestVideoWithRequest(TestVideoBase):
class TestVideoWithRequest(VideoTestBase):
async def test_send_all_args(self, bot, chat_id, video_file, video, thumb_file):
message = await bot.send_video(
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
class TestVideoNoteBase:
class VideoNoteTestBase:
length = 240
duration = 3
file_size = 132084
@ -60,7 +60,7 @@ class TestVideoNoteBase:
videonote_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestVideoNoteWithoutRequest(TestVideoNoteBase):
class TestVideoNoteWithoutRequest(VideoNoteTestBase):
def test_slot_behaviour(self, video_note):
for attr in video_note.__slots__:
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):
message = await bot.send_video_note(
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
class TestVoiceBase:
class VoiceTestBase:
duration = 3
mime_type = "audio/ogg"
file_size = 9199
@ -59,7 +59,7 @@ class TestVoiceBase:
voice_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestVoiceWithoutRequest(TestVoiceBase):
class TestVoiceWithoutRequest(VoiceTestBase):
def test_slot_behaviour(self, voice):
for attr in voice.__slots__:
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))
class TestVoiceWithRequest(TestVoiceBase):
class TestVoiceWithRequest(VoiceTestBase):
async def test_send_all_args(self, bot, chat_id, voice_file, voice):
message = await bot.send_voice(
chat_id,

View file

@ -26,18 +26,18 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def game():
game = Game(
TestGameBase.title,
TestGameBase.description,
TestGameBase.photo,
text=TestGameBase.text,
text_entities=TestGameBase.text_entities,
animation=TestGameBase.animation,
GameTestBase.title,
GameTestBase.description,
GameTestBase.photo,
text=GameTestBase.text,
text_entities=GameTestBase.text_entities,
animation=GameTestBase.animation,
)
game._unfreeze()
return game
class TestGameBase:
class GameTestBase:
title = "Python-telegram-bot Test Game"
description = "description"
photo = [PhotoSize("Blah", "ElseBlah", 640, 360, file_size=0)]
@ -49,7 +49,7 @@ class TestGameBase:
animation = Animation("blah", "unique_id", 320, 180, 1)
class TestGameWithoutRequest(TestGameBase):
class TestGameWithoutRequest(GameTestBase):
def test_slot_behaviour(self, game):
for attr in game.__slots__:
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")
def game_highscore():
return GameHighScore(
TestGameHighScoreBase.position, TestGameHighScoreBase.user, TestGameHighScoreBase.score
GameHighScoreTestBase.position, GameHighScoreTestBase.user, GameHighScoreTestBase.score
)
class TestGameHighScoreBase:
class GameHighScoreTestBase:
position = 12
user = User(2, "test user", False)
score = 42
class TestGameHighScoreWithoutRequest(TestGameHighScoreBase):
class TestGameHighScoreWithoutRequest(GameHighScoreTestBase):
def test_slot_behaviour(self, game_highscore):
for attr in game_highscore.__slots__:
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")
def inline_keyboard_button():
return InlineKeyboardButton(
TestInlineKeyboardButtonBase.text,
url=TestInlineKeyboardButtonBase.url,
callback_data=TestInlineKeyboardButtonBase.callback_data,
switch_inline_query=TestInlineKeyboardButtonBase.switch_inline_query,
InlineKeyboardButtonTestBase.text,
url=InlineKeyboardButtonTestBase.url,
callback_data=InlineKeyboardButtonTestBase.callback_data,
switch_inline_query=InlineKeyboardButtonTestBase.switch_inline_query,
switch_inline_query_current_chat=(
TestInlineKeyboardButtonBase.switch_inline_query_current_chat
InlineKeyboardButtonTestBase.switch_inline_query_current_chat
),
callback_game=TestInlineKeyboardButtonBase.callback_game,
pay=TestInlineKeyboardButtonBase.pay,
login_url=TestInlineKeyboardButtonBase.login_url,
web_app=TestInlineKeyboardButtonBase.web_app,
callback_game=InlineKeyboardButtonTestBase.callback_game,
pay=InlineKeyboardButtonTestBase.pay,
login_url=InlineKeyboardButtonTestBase.login_url,
web_app=InlineKeyboardButtonTestBase.web_app,
switch_inline_query_chosen_chat=(
TestInlineKeyboardButtonBase.switch_inline_query_chosen_chat
InlineKeyboardButtonTestBase.switch_inline_query_chosen_chat
),
)
class TestInlineKeyboardButtonBase:
class InlineKeyboardButtonTestBase:
text = "text"
url = "url"
callback_data = "callback data"
@ -62,7 +62,7 @@ class TestInlineKeyboardButtonBase:
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):
inst = inline_keyboard_button
for attr in inst.__slots__:

View file

@ -31,10 +31,10 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_keyboard_markup():
return InlineKeyboardMarkup(TestInlineKeyboardMarkupBase.inline_keyboard)
return InlineKeyboardMarkup(InlineKeyboardMarkupTestBase.inline_keyboard)
class TestInlineKeyboardMarkupBase:
class InlineKeyboardMarkupTestBase:
inline_keyboard = [
[
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):
inst = inline_keyboard_markup
for attr in inst.__slots__:
@ -228,7 +228,7 @@ class TestInlineKeyboardMarkupWithoutRequest(TestInlineKeyboardMarkupBase):
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(
self, bot, chat_id, inline_keyboard_markup
):

View file

@ -31,17 +31,17 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query(bot):
ilq = InlineQuery(
TestInlineQueryBase.id_,
TestInlineQueryBase.from_user,
TestInlineQueryBase.query,
TestInlineQueryBase.offset,
location=TestInlineQueryBase.location,
InlineQueryTestBase.id_,
InlineQueryTestBase.from_user,
InlineQueryTestBase.query,
InlineQueryTestBase.offset,
location=InlineQueryTestBase.location,
)
ilq.set_bot(bot)
return ilq
class TestInlineQueryBase:
class InlineQueryTestBase:
id_ = 1234
from_user = User(1, "First name", False)
query = "query text"
@ -49,7 +49,7 @@ class TestInlineQueryBase:
location = Location(8.8, 53.1)
class TestInlineQueryWithoutRequest(TestInlineQueryBase):
class TestInlineQueryWithoutRequest(InlineQueryTestBase):
def test_slot_behaviour(self, inline_query):
for attr in inline_query.__slots__:
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")
def inline_query_result_article():
return InlineQueryResultArticle(
TestInlineQueryResultArticleBase.id_,
TestInlineQueryResultArticleBase.title,
input_message_content=TestInlineQueryResultArticleBase.input_message_content,
reply_markup=TestInlineQueryResultArticleBase.reply_markup,
url=TestInlineQueryResultArticleBase.url,
hide_url=TestInlineQueryResultArticleBase.hide_url,
description=TestInlineQueryResultArticleBase.description,
thumbnail_url=TestInlineQueryResultArticleBase.thumbnail_url,
thumbnail_height=TestInlineQueryResultArticleBase.thumbnail_height,
thumbnail_width=TestInlineQueryResultArticleBase.thumbnail_width,
InlineQueryResultArticleTestBase.id_,
InlineQueryResultArticleTestBase.title,
input_message_content=InlineQueryResultArticleTestBase.input_message_content,
reply_markup=InlineQueryResultArticleTestBase.reply_markup,
url=InlineQueryResultArticleTestBase.url,
hide_url=InlineQueryResultArticleTestBase.hide_url,
description=InlineQueryResultArticleTestBase.description,
thumbnail_url=InlineQueryResultArticleTestBase.thumbnail_url,
thumbnail_height=InlineQueryResultArticleTestBase.thumbnail_height,
thumbnail_width=InlineQueryResultArticleTestBase.thumbnail_width,
)
class TestInlineQueryResultArticleBase:
class InlineQueryResultArticleTestBase:
id_ = "id"
type_ = "article"
title = "title"
@ -61,7 +61,7 @@ class TestInlineQueryResultArticleBase:
thumbnail_width = 15
class TestInlineQueryResultArticleWithoutRequest(TestInlineQueryResultArticleBase):
class TestInlineQueryResultArticleWithoutRequest(InlineQueryResultArticleTestBase):
def test_slot_behaviour(self, inline_query_result_article):
inst = inline_query_result_article
for attr in inst.__slots__:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -26,14 +26,14 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def input_text_message_content():
return InputTextMessageContent(
TestInputTextMessageContentBase.message_text,
parse_mode=TestInputTextMessageContentBase.parse_mode,
entities=TestInputTextMessageContentBase.entities,
link_preview_options=TestInputTextMessageContentBase.link_preview_options,
InputTextMessageContentTestBase.message_text,
parse_mode=InputTextMessageContentTestBase.parse_mode,
entities=InputTextMessageContentTestBase.entities,
link_preview_options=InputTextMessageContentTestBase.link_preview_options,
)
class TestInputTextMessageContentBase:
class InputTextMessageContentTestBase:
message_text = "*message text*"
parse_mode = ParseMode.MARKDOWN
entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)]
@ -41,7 +41,7 @@ class TestInputTextMessageContentBase:
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):
inst = input_text_message_content
for attr in inst.__slots__:

View file

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

View file

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

View file

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

View file

@ -220,7 +220,7 @@ def passport_data(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_unique_id = "d4e390cca57b4da5a65322b304762a12"
driver_license_front_side_file_id = "DgADBAADxwMAApnQgVPK2-ckL2eXVAI"
@ -243,7 +243,7 @@ class TestPassportBase:
driver_license_selfie_credentials_secret = "tivdId6RNYNsvXYPppdzrbxOBuBOr9wXRPDcCvnXU7E="
class TestPassportWithoutRequest(TestPassportBase):
class TestPassportWithoutRequest(PassportTestBase):
def test_slot_behaviour(self, passport_data):
inst = passport_data
for attr in inst.__slots__:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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