From 6ba7a097f4360a57df3c8d57b5ba0da6e7ea206a Mon Sep 17 00:00:00 2001 From: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com> Date: Wed, 22 May 2024 17:43:45 +0200 Subject: [PATCH 1/9] Update Settings for pre-commit.ci (#4265) --- .pre-commit-config.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a0e83a6521c..5760c9eac06 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,8 @@ ci: autofix_prs: false - autoupdate_schedule: monthly + autoupdate_schedule: quarterly + autoupdate_commit_msg: 'Bump `pre-commit` Hooks to Latest Versions' repos: - repo: https://github.com/astral-sh/ruff-pre-commit From c1c5438f37f582567f629cc1b4aa625ed17e6e82 Mon Sep 17 00:00:00 2001 From: Harshil <37377066+harshil21@users.noreply.github.com> Date: Wed, 29 May 2024 13:59:23 -0400 Subject: [PATCH 2/9] Remove Functionality Deprecated in Bot API 7.3 (#4266) --- docs/auxil/sphinx_hooks.py | 1 + docs/source/telegram.chat.rst | 2 + docs/source/telegram.chatfullinfo.rst | 4 +- docs/source/telegram.photosize.rst | 2 +- telegram/_bot.py | 8 +- telegram/_chat.py | 999 +++----------------------- telegram/_chatfullinfo.py | 470 ++++++++++-- telegram/_message.py | 2 +- telegram/constants.py | 5 +- tests/test_chat.py | 248 +------ tests/test_chatfullinfo.py | 202 ++++-- tests/test_official/exceptions.py | 5 +- tests/test_telegramobject.py | 6 +- 13 files changed, 694 insertions(+), 1260 deletions(-) diff --git a/docs/auxil/sphinx_hooks.py b/docs/auxil/sphinx_hooks.py index 3074ac7afb3..2cfbfe14049 100644 --- a/docs/auxil/sphinx_hooks.py +++ b/docs/auxil/sphinx_hooks.py @@ -46,6 +46,7 @@ "_BaseThumbedMedium": "TelegramObject", "_BaseMedium": "TelegramObject", "_CredentialsBase": "TelegramObject", + "_ChatBase": "TelegramObject", } diff --git a/docs/source/telegram.chat.rst b/docs/source/telegram.chat.rst index 3ef9672472f..d69b08b60e8 100644 --- a/docs/source/telegram.chat.rst +++ b/docs/source/telegram.chat.rst @@ -1,6 +1,8 @@ Chat ==== +.. Also lists methods of _ChatBase, but not the ones of TelegramObject .. autoclass:: telegram.Chat :members: :show-inheritance: + :inherited-members: TelegramObject diff --git a/docs/source/telegram.chatfullinfo.rst b/docs/source/telegram.chatfullinfo.rst index f15dbeedaa1..3bbc9fa9e18 100644 --- a/docs/source/telegram.chatfullinfo.rst +++ b/docs/source/telegram.chatfullinfo.rst @@ -1,6 +1,8 @@ ChatFullInfo ============ +.. Also lists methods of _ChatBase, but not the ones of TelegramObject .. autoclass:: telegram.ChatFullInfo :members: - :show-inheritance: \ No newline at end of file + :show-inheritance: + :inherited-members: TelegramObject \ No newline at end of file diff --git a/docs/source/telegram.photosize.rst b/docs/source/telegram.photosize.rst index d36e6e27fe4..be044f1164b 100644 --- a/docs/source/telegram.photosize.rst +++ b/docs/source/telegram.photosize.rst @@ -1,6 +1,6 @@ PhotoSize ========= -.. Also lists methods of _BaseThumbedMedium, but not the ones of TelegramObject +.. Also lists methods of _BaseMedium, but not the ones of TelegramObject .. autoclass:: telegram.PhotoSize :members: diff --git a/telegram/_bot.py b/telegram/_bot.py index cf08284c7fe..3ae0ce5f17d 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -1152,7 +1152,7 @@ async def forward_message( Note: Since the release of Bot API 5.5 it can be impossible to forward messages from some chats. Use the attributes :attr:`telegram.Message.has_protected_content` and - :attr:`telegram.Chat.has_protected_content` to check this. + :attr:`telegram.ChatFullInfo.has_protected_content` to check this. As a workaround, it is still possible to use :meth:`copy_message`. However, this behaviour is undocumented and might be changed by Telegram. @@ -4610,8 +4610,8 @@ async def set_chat_sticker_set( ) -> bool: """Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate - admin rights. Use the field :attr:`telegram.Chat.can_set_sticker_set` optionally returned - in :meth:`get_chat` requests to check if the bot can use this method. + admin rights. Use the field :attr:`telegram.ChatFullInfo.can_set_sticker_set` optionally + returned in :meth:`get_chat` requests to check if the bot can use this method. Args: chat_id (:obj:`int` | :obj:`str`): |chat_id_group| @@ -4644,7 +4644,7 @@ async def delete_chat_sticker_set( ) -> bool: """Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. - Use the field :attr:`telegram.Chat.can_set_sticker_set` optionally returned in + Use the field :attr:`telegram.ChatFullInfo.can_set_sticker_set` optionally returned in :meth:`get_chat` requests to check if the bot can use this method. Args: diff --git a/telegram/_chat.py b/telegram/_chat.py index b94d006e141..8250a8f179b 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -20,36 +20,25 @@ """This module contains an object that represents a Telegram Chat.""" from datetime import datetime from html import escape -from typing import TYPE_CHECKING, Any, Final, Optional, Sequence, Tuple, Union +from typing import TYPE_CHECKING, Final, Optional, Sequence, Tuple, Union from telegram import constants -from telegram._birthdate import Birthdate -from telegram._chatlocation import ChatLocation from telegram._chatpermissions import ChatPermissions -from telegram._files.chatphoto import ChatPhoto from telegram._forumtopic import ForumTopic from telegram._menubutton import MenuButton from telegram._reaction import ReactionType from telegram._telegramobject import TelegramObject from telegram._utils import enum -from telegram._utils.argumentparsing import parse_sequence_arg -from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import CorrectOptionID, FileInput, JSONDict, ODVInput, ReplyMarkup -from telegram._utils.warnings import warn from telegram.helpers import escape_markdown from telegram.helpers import mention_html as helpers_mention_html from telegram.helpers import mention_markdown as helpers_mention_markdown -from telegram.warnings import PTBDeprecationWarning if TYPE_CHECKING: from telegram import ( Animation, Audio, - Bot, - BusinessIntro, - BusinessLocation, - BusinessOpeningHours, ChatInviteLink, ChatMember, Contact, @@ -77,735 +66,13 @@ ) -_deprecated_attrs = ( - "accent_color_id", - "active_usernames", - "available_reactions", - "background_custom_emoji_id", - "bio", - "birthdate", - "business_intro", - "business_location", - "business_opening_hours", - "can_set_sticker_set", - "custom_emoji_sticker_set_name", - "description", - "emoji_status_custom_emoji_id", - "emoji_status_expiration_date", - "has_aggressive_anti_spam_enabled", - "has_hidden_members", - "has_private_forwards", - "has_protected_content", - "has_restricted_voice_and_video_messages", - "has_visible_history", - "invite_link", - "join_by_request", - "join_to_send_messages", - "linked_chat_id", - "location", - "message_auto_delete_time", - "permissions", - "personal_chat", - "photo", - "pinned_message", - "profile_accent_color_id", - "profile_background_custom_emoji_id", - "slow_mode_delay", - "sticker_set_name", - "unrestrict_boost_count", -) - - -class Chat(TelegramObject): - """This object represents a chat. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`id` is equal. - - .. versionchanged:: 20.0 - - * Removed the deprecated methods ``kick_member`` and ``get_members_count``. - * The following are now keyword-only arguments in Bot methods: - ``location``, ``filename``, ``contact``, ``{read, write, connect, pool}_timeout``, - ``api_kwargs``. Use a named argument for those, - and notice that some positional arguments changed position as a result. - - .. versionchanged:: 20.0 - Removed the attribute ``all_members_are_administrators``. As long as Telegram provides - this field for backwards compatibility, it is available through - :attr:`~telegram.TelegramObject.api_kwargs`. - - Args: - id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits - and some programming languages may have difficulty/silent defects in interpreting it. - But it is smaller than 52 bits, so a signed 64-bit integer or double-precision float - type are safe for storing this identifier. - type (:obj:`str`): Type of chat, can be either :attr:`PRIVATE`, :attr:`GROUP`, - :attr:`SUPERGROUP` or :attr:`CHANNEL`. - title (:obj:`str`, optional): Title, for supergroups, channels and group chats. - username (:obj:`str`, optional): Username, for private chats, supergroups and channels if - available. - first_name (:obj:`str`, optional): First name of the other party in a private chat. - last_name (:obj:`str`, optional): Last name of the other party in a private chat. - photo (:class:`telegram.ChatPhoto`, optional): Chat photo. - Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - bio (:obj:`str`, optional): Bio of the other party in a private chat. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - has_private_forwards (:obj:`bool`, optional): :obj:`True`, if privacy settings of the other - party in the private chat allows to use ``tg://user?id=`` links only in chats - with the user. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 13.9 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - description (:obj:`str`, optional): Description, for groups, supergroups and channel chats. - Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - invite_link (:obj:`str`, optional): Primary invite link, for groups, supergroups and - channel. Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - pinned_message (:class:`telegram.Message`, optional): The most recent pinned message - (by sending date). Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions, - for groups and supergroups. Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - slow_mode_delay (:obj:`int`, optional): For supergroups, the minimum allowed delay between - consecutive messages sent by each unprivileged user. - Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - message_auto_delete_time (:obj:`int`, optional): The time after which all messages sent to - the chat will be automatically deleted; in seconds. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 13.4 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - has_protected_content (:obj:`bool`, optional): :obj:`True`, if messages from the chat can't - be forwarded to other chats. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 13.9 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - has_visible_history (:obj:`bool`, optional): :obj:`True`, if new chat members will have - access to old messages; available only to chat administrators. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.8 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - sticker_set_name (:obj:`str`, optional): For supergroups, name of group sticker set. - Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - can_set_sticker_set (:obj:`bool`, optional): :obj:`True`, if the bot can change group the - sticker set. Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - linked_chat_id (:obj:`int`, optional): Unique identifier for the linked chat, i.e. the - discussion group identifier for a channel and vice versa; for supergroups and channel - chats. Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - location (:class:`telegram.ChatLocation`, optional): For supergroups, the location to which - the supergroup is connected. Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - join_to_send_messages (:obj:`bool`, optional): :obj:`True`, if users need to join the - supergroup before they can send messages. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - join_by_request (:obj:`bool`, optional): :obj:`True`, if all users directly joining the - supergroup without using an invite link need to be approved by supergroup - administrators. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - has_restricted_voice_and_video_messages (:obj:`bool`, optional): :obj:`True`, if the - privacy settings of the other party restrict sending voice and video note messages - in the private chat. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - is_forum (:obj:`bool`, optional): :obj:`True`, if the supergroup chat is a forum - (has topics_ enabled). - - .. versionadded:: 20.0 - active_usernames (Sequence[:obj:`str`], optional): If set, the list of all `active chat - usernames `_; for private chats, supergroups and channels. Returned - only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - business_intro (:class:`telegram.BusinessIntro`, optional): For private chats with - business accounts, the intro of the business. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 21.1 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - business_location (:class:`telegram.BusinessLocation`, optional): For private chats with - business accounts, the location of the business. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 21.1 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - business_opening_hours (:class:`telegram.BusinessOpeningHours`, optional): For private - chats with business accounts, the opening hours of the business. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 21.1 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - available_reactions (Sequence[:class:`telegram.ReactionType`], optional): List of available - reactions allowed in the chat. If omitted, then all of - :const:`telegram.constants.ReactionEmoji` are allowed. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.8 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - accent_color_id (:obj:`int`, optional): Identifier of the - :class:`accent color ` for the chat name and - backgrounds of the chat photo, reply header, and link preview. See `accent colors`_ - for more details. Returned only in :meth:`telegram.Bot.get_chat`. Always returned in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.8 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - background_custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of emoji chosen - by the chat for the reply header and link preview background. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.8 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - profile_accent_color_id (:obj:`int`, optional): Identifier of the - :class:`accent color ` for the chat's profile - background. See profile `accent colors`_ for more details. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.8 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - profile_background_custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of - the emoji chosen by the chat for its profile background. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.8 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - emoji_status_custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of emoji - status of the chat or the other party in a private chat. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - emoji_status_expiration_date (:class:`datetime.datetime`, optional): Expiration date of - emoji status of the chat or the other party in a private chat, in seconds. Returned - only in :meth:`telegram.Bot.get_chat`. - |datetime_localization| - - .. versionadded:: 20.5 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - has_aggressive_anti_spam_enabled (:obj:`bool`, optional): :obj:`True`, if aggressive - anti-spam checks are enabled in the supergroup. The field is only available to chat - administrators. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - has_hidden_members (:obj:`bool`, optional): :obj:`True`, if non-administrators can only - get the list of bots and administrators in the chat. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - unrestrict_boost_count (:obj:`int`, optional): For supergroups, the minimum number of - boosts that a non-administrator user needs to add in order to ignore slow mode and chat - permissions. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 21.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - custom_emoji_sticker_set_name (:obj:`str`, optional): For supergroups, the name of the - group's custom emoji sticker set. Custom emoji from this set can be used by all users - and bots in the group. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 21.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - birthdate (:obj:`telegram.Birthdate`, optional): For private chats, - the date of birth of the user. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 21.1 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - personal_chat (:obj:`telegram.Chat`, optional): For private chats, the personal channel of - the user. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 21.1 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - - Attributes: - id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits - and some programming languages may have difficulty/silent defects in interpreting it. - But it is smaller than 52 bits, so a signed 64-bit integer or double-precision float - type are safe for storing this identifier. - type (:obj:`str`): Type of chat, can be either :attr:`PRIVATE`, :attr:`GROUP`, - :attr:`SUPERGROUP` or :attr:`CHANNEL`. - title (:obj:`str`): Optional. Title, for supergroups, channels and group chats. - username (:obj:`str`): Optional. Username, for private chats, supergroups and channels if - available. - first_name (:obj:`str`): Optional. First name of the other party in a private chat. - last_name (:obj:`str`): Optional. Last name of the other party in a private chat. - photo (:class:`telegram.ChatPhoto`): Optional. Chat photo. - Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - bio (:obj:`str`): Optional. Bio of the other party in a private chat. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - has_private_forwards (:obj:`bool`): Optional. :obj:`True`, if privacy settings of the other - party in the private chat allows to use ``tg://user?id=`` links only in chats - with the user. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 13.9 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - description (:obj:`str`): Optional. Description, for groups, supergroups and channel chats. - Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - invite_link (:obj:`str`): Optional. Primary invite link, for groups, supergroups and - channel. Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - pinned_message (:class:`telegram.Message`): Optional. The most recent pinned message - (by sending date). Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions, - for groups and supergroups. Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - slow_mode_delay (:obj:`int`): Optional. For supergroups, the minimum allowed delay between - consecutive messages sent by each unprivileged user. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - message_auto_delete_time (:obj:`int`): Optional. The time after which all messages sent to - the chat will be automatically deleted; in seconds. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 13.4 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - has_protected_content (:obj:`bool`): Optional. :obj:`True`, if messages from the chat can't - be forwarded to other chats. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 13.9 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - has_visible_history (:obj:`bool`): Optional. :obj:`True`, if new chat members will have - access to old messages; available only to chat administrators. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.8 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - sticker_set_name (:obj:`str`): Optional. For supergroups, name of Group sticker set. - Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - can_set_sticker_set (:obj:`bool`): Optional. :obj:`True`, if the bot can change group the - sticker set. Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - linked_chat_id (:obj:`int`): Optional. Unique identifier for the linked chat, i.e. the - discussion group identifier for a channel and vice versa; for supergroups and channel - chats. Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - location (:class:`telegram.ChatLocation`): Optional. For supergroups, the location to which - the supergroup is connected. Returned only in :meth:`telegram.Bot.get_chat`. - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - join_to_send_messages (:obj:`bool`): Optional. :obj:`True`, if users need to join - the supergroup before they can send messages. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - join_by_request (:obj:`bool`): Optional. :obj:`True`, if all users directly joining the - supergroup without using an invite link need to be approved by supergroup - administrators. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - has_restricted_voice_and_video_messages (:obj:`bool`): Optional. :obj:`True`, if the - privacy settings of the other party restrict sending voice and video note messages - in the private chat. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - is_forum (:obj:`bool`): Optional. :obj:`True`, if the supergroup chat is a forum - (has topics_ enabled). - - .. versionadded:: 20.0 - active_usernames (Tuple[:obj:`str`]): Optional. If set, the list of all `active chat - usernames `_; for private chats, supergroups and channels. Returned - only in :meth:`telegram.Bot.get_chat`. - This list is empty if the chat has no active usernames or this chat instance was not - obtained via :meth:`~telegram.Bot.get_chat`. - - .. versionadded:: 20.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - business_intro (:class:`telegram.BusinessIntro`): Optional. For private chats with - business accounts, the intro of the business. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 21.1 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - business_location (:class:`telegram.BusinessLocation`): Optional. For private chats with - business accounts, the location of the business. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 21.1 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - business_opening_hours (:class:`telegram.BusinessOpeningHours`): Optional. For private - chats with business accounts, the opening hours of the business. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 21.1 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - available_reactions (Tuple[:class:`telegram.ReactionType`]): Optional. List of available - reactions allowed in the chat. If omitted, then all of - :const:`telegram.constants.ReactionEmoji` are allowed. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.8 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - accent_color_id (:obj:`int`): Optional. Identifier of the - :class:`accent color ` for the chat name and - backgrounds of the chat photo, reply header, and link preview. See `accent colors`_ - for more details. Returned only in :meth:`telegram.Bot.get_chat`. Always returned in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.8 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - background_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of emoji chosen - by the chat for the reply header and link preview background. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.8 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - profile_accent_color_id (:obj:`int`): Optional. Identifier of the - :class:`accent color ` for the chat's profile - background. See profile `accent colors`_ for more details. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.8 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - profile_background_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of - the emoji chosen by the chat for its profile background. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.8 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - emoji_status_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of emoji - status of the chat or the other party in a private chat. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - emoji_status_expiration_date (:class:`datetime.datetime`): Optional. Expiration date of - emoji status of the chat or the other party in a private chat, in seconds. Returned - only in :meth:`telegram.Bot.get_chat`. - |datetime_localization| - - .. versionadded:: 20.5 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - has_aggressive_anti_spam_enabled (:obj:`bool`): Optional. :obj:`True`, if aggressive - anti-spam checks are enabled in the supergroup. The field is only available to chat - administrators. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - has_hidden_members (:obj:`bool`): Optional. :obj:`True`, if non-administrators can only - get the list of bots and administrators in the chat. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 20.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - unrestrict_boost_count (:obj:`int`): Optional. For supergroups, the minimum number of - boosts that a non-administrator user needs to add in order to ignore slow mode and chat - permissions. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 21.0 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - custom_emoji_sticker_set_name (:obj:`str`): Optional. For supergroups, the name of the - group's custom emoji sticker set. Custom emoji from this set can be used by all users - and bots in the group. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 21.0 +class _ChatBase(TelegramObject): + """Base class for :class:`telegram.Chat` and :class:`telegram.ChatFullInfo`. - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - birthdate (:obj:`telegram.Birthdate`): Optional. For private chats, - the date of birth of the user. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 21.1 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - personal_chat (:obj:`telegram.Chat`): Optional. For private chats, the personal channel of - the user. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 21.1 - - .. deprecated:: 21.2 - In accordance to Bot API 7.3, this attribute will be moved to - :class:`telegram.ChatFullInfo`. - - .. _topics: https://telegram.org/blog/topics-in-groups-collectible-usernames#topics-in-groups - .. _accent colors: https://core.telegram.org/bots/api#accent-colors + .. versionadded:: NEXT.VERSION """ - __slots__ = ( - "accent_color_id", - "active_usernames", - "available_reactions", - "background_custom_emoji_id", - "bio", - "birthdate", - "business_intro", - "business_location", - "business_opening_hours", - "can_set_sticker_set", - "custom_emoji_sticker_set_name", - "description", - "emoji_status_custom_emoji_id", - "emoji_status_expiration_date", - "first_name", - "has_aggressive_anti_spam_enabled", - "has_hidden_members", - "has_private_forwards", - "has_protected_content", - "has_restricted_voice_and_video_messages", - "has_visible_history", - "id", - "invite_link", - "is_forum", - "join_by_request", - "join_to_send_messages", - "last_name", - "linked_chat_id", - "location", - "message_auto_delete_time", - "permissions", - "personal_chat", - "photo", - "pinned_message", - "profile_accent_color_id", - "profile_background_custom_emoji_id", - "slow_mode_delay", - "sticker_set_name", - "title", - "type", - "unrestrict_boost_count", - "username", - ) - SENDER: Final[str] = constants.ChatType.SENDER - """:const:`telegram.constants.ChatType.SENDER` - - .. versionadded:: 13.5 - """ - PRIVATE: Final[str] = constants.ChatType.PRIVATE - """:const:`telegram.constants.ChatType.PRIVATE`""" - GROUP: Final[str] = constants.ChatType.GROUP - """:const:`telegram.constants.ChatType.GROUP`""" - SUPERGROUP: Final[str] = constants.ChatType.SUPERGROUP - """:const:`telegram.constants.ChatType.SUPERGROUP`""" - CHANNEL: Final[str] = constants.ChatType.CHANNEL - """:const:`telegram.constants.ChatType.CHANNEL`""" + __slots__ = ("first_name", "id", "is_forum", "last_name", "title", "type", "username") def __init__( self, @@ -815,42 +82,7 @@ def __init__( username: Optional[str] = None, first_name: Optional[str] = None, last_name: Optional[str] = None, - photo: Optional[ChatPhoto] = None, - description: Optional[str] = None, - invite_link: Optional[str] = None, - pinned_message: Optional["Message"] = None, - permissions: Optional[ChatPermissions] = None, - sticker_set_name: Optional[str] = None, - can_set_sticker_set: Optional[bool] = None, - slow_mode_delay: Optional[int] = None, - bio: Optional[str] = None, - linked_chat_id: Optional[int] = None, - location: Optional[ChatLocation] = None, - message_auto_delete_time: Optional[int] = None, - has_private_forwards: Optional[bool] = None, - has_protected_content: Optional[bool] = None, - join_to_send_messages: Optional[bool] = None, - join_by_request: Optional[bool] = None, - has_restricted_voice_and_video_messages: Optional[bool] = None, is_forum: Optional[bool] = None, - active_usernames: Optional[Sequence[str]] = None, - emoji_status_custom_emoji_id: Optional[str] = None, - emoji_status_expiration_date: Optional[datetime] = None, - has_aggressive_anti_spam_enabled: Optional[bool] = None, - has_hidden_members: Optional[bool] = None, - available_reactions: Optional[Sequence[ReactionType]] = None, - accent_color_id: Optional[int] = None, # required in API 7.3 - Optional for back compat - background_custom_emoji_id: Optional[str] = None, - profile_accent_color_id: Optional[int] = None, - profile_background_custom_emoji_id: Optional[str] = None, - has_visible_history: Optional[bool] = None, - unrestrict_boost_count: Optional[int] = None, - custom_emoji_sticker_set_name: Optional[str] = None, - birthdate: Optional[Birthdate] = None, - personal_chat: Optional["Chat"] = None, - business_intro: Optional["BusinessIntro"] = None, - business_location: Optional["BusinessLocation"] = None, - business_opening_hours: Optional["BusinessOpeningHours"] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -863,82 +95,31 @@ def __init__( self.username: Optional[str] = username self.first_name: Optional[str] = first_name self.last_name: Optional[str] = last_name - self.photo: Optional[ChatPhoto] = photo - self.bio: Optional[str] = bio - self.has_private_forwards: Optional[bool] = has_private_forwards - self.description: Optional[str] = description - self.invite_link: Optional[str] = invite_link - self.pinned_message: Optional[Message] = pinned_message - self.permissions: Optional[ChatPermissions] = permissions - self.slow_mode_delay: Optional[int] = slow_mode_delay - self.message_auto_delete_time: Optional[int] = ( - int(message_auto_delete_time) if message_auto_delete_time is not None else None - ) - self.has_protected_content: Optional[bool] = has_protected_content - self.has_visible_history: Optional[bool] = has_visible_history - self.sticker_set_name: Optional[str] = sticker_set_name - self.can_set_sticker_set: Optional[bool] = can_set_sticker_set - self.linked_chat_id: Optional[int] = linked_chat_id - self.location: Optional[ChatLocation] = location - self.join_to_send_messages: Optional[bool] = join_to_send_messages - self.join_by_request: Optional[bool] = join_by_request - self.has_restricted_voice_and_video_messages: Optional[bool] = ( - has_restricted_voice_and_video_messages - ) self.is_forum: Optional[bool] = is_forum - self.active_usernames: Tuple[str, ...] = parse_sequence_arg(active_usernames) - self.emoji_status_custom_emoji_id: Optional[str] = emoji_status_custom_emoji_id - self.emoji_status_expiration_date: Optional[datetime] = emoji_status_expiration_date - self.has_aggressive_anti_spam_enabled: Optional[bool] = has_aggressive_anti_spam_enabled - self.has_hidden_members: Optional[bool] = has_hidden_members - self.available_reactions: Optional[Tuple[ReactionType, ...]] = parse_sequence_arg( - available_reactions - ) - self.accent_color_id: Optional[int] = accent_color_id - self.background_custom_emoji_id: Optional[str] = background_custom_emoji_id - self.profile_accent_color_id: Optional[int] = profile_accent_color_id - self.profile_background_custom_emoji_id: Optional[str] = profile_background_custom_emoji_id - self.unrestrict_boost_count: Optional[int] = unrestrict_boost_count - self.custom_emoji_sticker_set_name: Optional[str] = custom_emoji_sticker_set_name - self.birthdate: Optional[Birthdate] = birthdate - self.personal_chat: Optional["Chat"] = personal_chat - self.business_intro: Optional["BusinessIntro"] = business_intro - self.business_location: Optional["BusinessLocation"] = business_location - self.business_opening_hours: Optional["BusinessOpeningHours"] = business_opening_hours - - if self.__class__ is Chat: - for arg in _deprecated_attrs: - if (val := object.__getattribute__(self, arg)) is not None and val != (): - warn( - PTBDeprecationWarning( - "21.2", - f"The argument `{arg}` is deprecated and will only be available via " - "`ChatFullInfo` in the future.", - ), - stacklevel=2, - ) self._id_attrs = (self.id,) self._freeze() - def __getattribute__(self, name: str) -> Any: - if name in _deprecated_attrs and self.__class__ is Chat: - warn( - PTBDeprecationWarning( - "21.2", - f"The attribute `{name}` is deprecated and will only be accessible via " - "`ChatFullInfo` in the future.", - ), - stacklevel=2, - ) - return super().__getattribute__(name) + SENDER: Final[str] = constants.ChatType.SENDER + """:const:`telegram.constants.ChatType.SENDER` + + .. versionadded:: 13.5 + """ + PRIVATE: Final[str] = constants.ChatType.PRIVATE + """:const:`telegram.constants.ChatType.PRIVATE`""" + GROUP: Final[str] = constants.ChatType.GROUP + """:const:`telegram.constants.ChatType.GROUP`""" + SUPERGROUP: Final[str] = constants.ChatType.SUPERGROUP + """:const:`telegram.constants.ChatType.SUPERGROUP`""" + CHANNEL: Final[str] = constants.ChatType.CHANNEL + """:const:`telegram.constants.ChatType.CHANNEL`""" @property def effective_name(self) -> Optional[str]: """ - :obj:`str`: Convenience property. Gives :attr:`title` if not :obj:`None`, - else :attr:`full_name` if not :obj:`None`. + :obj:`str`: Convenience property. Gives :attr:`~Chat.title` if not :obj:`None`, + else :attr:`~Chat.full_name` if not :obj:`None`. .. versionadded:: 20.1 """ @@ -951,8 +132,8 @@ def effective_name(self) -> Optional[str]: @property def full_name(self) -> Optional[str]: """ - :obj:`str`: Convenience property. If :attr:`first_name` is not :obj:`None`, gives - :attr:`first_name` followed by (if available) :attr:`last_name`. + :obj:`str`: Convenience property. If :attr:`~Chat.first_name` is not :obj:`None`, gives + :attr:`~Chat.first_name` followed by (if available) :attr:`~Chat.last_name`. Note: :attr:`full_name` will always be :obj:`None`, if the chat is a (super)group or @@ -968,58 +149,13 @@ def full_name(self) -> Optional[str]: @property def link(self) -> Optional[str]: - """:obj:`str`: Convenience property. If the chat has a :attr:`username`, returns a t.me - link of the chat. + """:obj:`str`: Convenience property. If the chat has a :attr:`~Chat.username`, returns a + t.me link of the chat. """ if self.username: return f"https://t.me/{self.username}" return None - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Chat"]: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - # Get the local timezone from the bot if it has defaults - loc_tzinfo = extract_tzinfo_from_defaults(bot) - - data["emoji_status_expiration_date"] = from_timestamp( - data.get("emoji_status_expiration_date"), tzinfo=loc_tzinfo - ) - - data["photo"] = ChatPhoto.de_json(data.get("photo"), bot) - from telegram import ( # pylint: disable=import-outside-toplevel - BusinessIntro, - BusinessLocation, - BusinessOpeningHours, - Message, - ) - - data["pinned_message"] = Message.de_json(data.get("pinned_message"), bot) - data["permissions"] = ChatPermissions.de_json(data.get("permissions"), bot) - data["location"] = ChatLocation.de_json(data.get("location"), bot) - data["available_reactions"] = ReactionType.de_list(data.get("available_reactions"), bot) - data["birthdate"] = Birthdate.de_json(data.get("birthdate"), bot) - data["personal_chat"] = Chat.de_json(data.get("personal_chat"), bot) - data["business_intro"] = BusinessIntro.de_json(data.get("business_intro"), bot) - data["business_location"] = BusinessLocation.de_json(data.get("business_location"), bot) - data["business_opening_hours"] = BusinessOpeningHours.de_json( - data.get("business_opening_hours"), bot - ) - - api_kwargs = {} - # This is a deprecated field that TG still returns for backwards compatibility - # Let's filter it out to speed up the de-json process - if "all_members_are_administrators" in data: - api_kwargs["all_members_are_administrators"] = data.pop( - "all_members_are_administrators" - ) - - return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs) - def mention_markdown(self, name: Optional[str] = None) -> str: """ Note: @@ -1030,17 +166,18 @@ def mention_markdown(self, name: Optional[str] = None) -> str: .. versionadded:: 20.0 Args: - name (:obj:`str`): The name used as a link for the chat. Defaults to :attr:`full_name`. + name (:obj:`str`): The name used as a link for the chat. Defaults to + :attr:`~Chat.full_name`. Returns: :obj:`str`: The inline mention for the chat as markdown (version 1). Raises: :exc:`TypeError`: If the chat is a private chat and neither the :paramref:`name` - nor the :attr:`first_name` is set, then throw an :exc:`TypeError`. - If the chat is a public chat and neither the :paramref:`name` nor the :attr:`title` - is set, then throw an :exc:`TypeError`. If chat is a private group chat, then - throw an :exc:`TypeError`. + nor the :attr:`~Chat.first_name` is set, then throw an :exc:`TypeError`. + If the chat is a public chat and neither the :paramref:`name` nor the + :attr:`~Chat.title` is set, then throw an :exc:`TypeError`. If chat is a + private group chat, then throw an :exc:`TypeError`. """ if self.type == self.PRIVATE: @@ -1062,17 +199,18 @@ def mention_markdown_v2(self, name: Optional[str] = None) -> str: .. versionadded:: 20.0 Args: - name (:obj:`str`): The name used as a link for the chat. Defaults to :attr:`full_name`. + name (:obj:`str`): The name used as a link for the chat. Defaults to + :attr:`~Chat.full_name`. Returns: :obj:`str`: The inline mention for the chat as markdown (version 2). Raises: :exc:`TypeError`: If the chat is a private chat and neither the :paramref:`name` - nor the :attr:`first_name` is set, then throw an :exc:`TypeError`. - If the chat is a public chat and neither the :paramref:`name` nor the :attr:`title` - is set, then throw an :exc:`TypeError`. If chat is a private group chat, then - throw an :exc:`TypeError`. + nor the :attr:`~Chat.first_name` is set, then throw an :exc:`TypeError`. + If the chat is a public chat and neither the :paramref:`name` nor the + :attr:`~Chat.title` is set, then throw an :exc:`TypeError`. If chat is a + private group chat, then throw an :exc:`TypeError`. """ if self.type == self.PRIVATE: @@ -1101,10 +239,10 @@ def mention_html(self, name: Optional[str] = None) -> str: Raises: :exc:`TypeError`: If the chat is a private chat and neither the :paramref:`name` - nor the :attr:`first_name` is set, then throw an :exc:`TypeError`. - If the chat is a public chat and neither the :paramref:`name` nor the :attr:`title` - is set, then throw an :exc:`TypeError`. If chat is a private group chat, then - throw an :exc:`TypeError`. + nor the :attr:`~Chat.first_name` is set, then throw an :exc:`TypeError`. + If the chat is a public chat and neither the :paramref:`name` nor the + :attr:`~Chat.title` is set, then throw an :exc:`TypeError`. + If chat is a private group chat, then throw an :exc:`TypeError`. """ if self.type == self.PRIVATE: @@ -4074,3 +3212,60 @@ async def set_message_reaction( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) + + +class Chat(_ChatBase): + """This object represents a chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`id` is equal. + + .. versionchanged:: 20.0 + + * Removed the deprecated methods ``kick_member`` and ``get_members_count``. + * The following are now keyword-only arguments in Bot methods: + ``location``, ``filename``, ``contact``, ``{read, write, connect, pool}_timeout``, + ``api_kwargs``. Use a named argument for those, + and notice that some positional arguments changed position as a result. + + .. versionchanged:: 20.0 + Removed the attribute ``all_members_are_administrators``. As long as Telegram provides + this field for backwards compatibility, it is available through + :attr:`~telegram.TelegramObject.api_kwargs`. + + .. versionchanged:: NEXT.VERSION + As per Bot API 7.3, most of the arguments and attributes of this class have now moved to + :class:`telegram.ChatFullInfo`. + + Args: + id (:obj:`int`): Unique identifier for this chat. + type (:obj:`str`): Type of chat, can be either :attr:`PRIVATE`, :attr:`GROUP`, + :attr:`SUPERGROUP` or :attr:`CHANNEL`. + title (:obj:`str`, optional): Title, for supergroups, channels and group chats. + username (:obj:`str`, optional): Username, for private chats, supergroups and channels if + available. + first_name (:obj:`str`, optional): First name of the other party in a private chat. + last_name (:obj:`str`, optional): Last name of the other party in a private chat. + is_forum (:obj:`bool`, optional): :obj:`True`, if the supergroup chat is a forum + (has topics_ enabled). + + .. versionadded:: 20.0 + + Attributes: + id (:obj:`int`): Unique identifier for this chat. + type (:obj:`str`): Type of chat, can be either :attr:`PRIVATE`, :attr:`GROUP`, + :attr:`SUPERGROUP` or :attr:`CHANNEL`. + title (:obj:`str`): Optional. Title, for supergroups, channels and group chats. + username (:obj:`str`): Optional. Username, for private chats, supergroups and channels if + available. + first_name (:obj:`str`): Optional. First name of the other party in a private chat. + last_name (:obj:`str`): Optional. Last name of the other party in a private chat. + is_forum (:obj:`bool`): Optional. :obj:`True`, if the supergroup chat is a forum + (has topics_ enabled). + + .. versionadded:: 20.0 + + .. _topics: https://telegram.org/blog/topics-in-groups-collectible-usernames#topics-in-groups + """ + + __slots__ = () diff --git a/telegram/_chatfullinfo.py b/telegram/_chatfullinfo.py index 9b100830bfc..221b8f623cc 100644 --- a/telegram/_chatfullinfo.py +++ b/telegram/_chatfullinfo.py @@ -19,58 +19,380 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChatFullInfo.""" from datetime import datetime -from typing import TYPE_CHECKING, Optional, Sequence +from typing import TYPE_CHECKING, Optional, Sequence, Tuple from telegram._birthdate import Birthdate -from telegram._chat import Chat +from telegram._chat import Chat, _ChatBase from telegram._chatlocation import ChatLocation from telegram._chatpermissions import ChatPermissions from telegram._files.chatphoto import ChatPhoto from telegram._reaction import ReactionType +from telegram._utils.argumentparsing import parse_sequence_arg +from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp from telegram._utils.types import JSONDict if TYPE_CHECKING: - from telegram import BusinessIntro, BusinessLocation, BusinessOpeningHours, Message + from telegram import Bot, BusinessIntro, BusinessLocation, BusinessOpeningHours, Message -class ChatFullInfo(Chat): +class ChatFullInfo(_ChatBase): """ This object contains full information about a chat. Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`~telegram.Chat.id` is equal. - Caution: - This class is a subclass of :class:`telegram.Chat` and inherits all attributes and methods - for backwards compatibility. In the future, this class will *NOT* inherit from - :class:`telegram.Chat`. - - .. seealso:: - All arguments and attributes can be found in :class:`telegram.Chat`. - .. versionadded:: 21.2 + .. versionchanged:: NEXT.VERSION + Explicit support for all shortcut methods known from :class:`telegram.Chat` on this + object. Previously those were only available because this class inherited from + :class:`telegram.Chat`. + Args: + id (:obj:`int`): Unique identifier for this chat. + type (:obj:`str`): Type of chat, can be either :attr:`PRIVATE`, :attr:`GROUP`, + :attr:`SUPERGROUP` or :attr:`CHANNEL`. + accent_color_id (:obj:`int`, optional): Identifier of the + :class:`accent color ` for the chat name and + backgrounds of the chat photo, reply header, and link preview. See `accent colors`_ + for more details. + + .. versionadded:: 20.8 max_reaction_count (:obj:`int`): The maximum number of reactions that can be set on a message in the chat. .. versionadded:: 21.2 + title (:obj:`str`, optional): Title, for supergroups, channels and group chats. + username (:obj:`str`, optional): Username, for private chats, supergroups and channels if + available. + first_name (:obj:`str`, optional): First name of the other party in a private chat. + last_name (:obj:`str`, optional): Last name of the other party in a private chat. + is_forum (:obj:`bool`, optional): :obj:`True`, if the supergroup chat is a forum + (has topics_ enabled). + + .. versionadded:: 20.0 + photo (:class:`telegram.ChatPhoto`, optional): Chat photo. + active_usernames (Sequence[:obj:`str`], optional): If set, the list of all `active chat + usernames `_; for private chats, supergroups and channels. + + .. versionadded:: 20.0 + birthdate (:obj:`telegram.Birthdate`, optional): For private chats, + the date of birth of the user. + + .. versionadded:: 21.1 + business_intro (:class:`telegram.BusinessIntro`, optional): For private chats with + business accounts, the intro of the business. + + .. versionadded:: 21.1 + business_location (:class:`telegram.BusinessLocation`, optional): For private chats with + business accounts, the location of the business. + + .. versionadded:: 21.1 + business_opening_hours (:class:`telegram.BusinessOpeningHours`, optional): For private + chats with business accounts, the opening hours of the business. + + .. versionadded:: 21.1 + personal_chat (:obj:`telegram.Chat`, optional): For private chats, the personal channel of + the user. + + .. versionadded:: 21.1 + available_reactions (Sequence[:class:`telegram.ReactionType`], optional): List of available + reactions allowed in the chat. If omitted, then all of + :const:`telegram.constants.ReactionEmoji` are allowed. + + .. versionadded:: 20.8 + background_custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of emoji chosen + by the chat for the reply header and link preview background. + + .. versionadded:: 20.8 + profile_accent_color_id (:obj:`int`, optional): Identifier of the + :class:`accent color ` for the chat's profile + background. See profile `accent colors`_ for more details. + + .. versionadded:: 20.8 + profile_background_custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of + the emoji chosen by the chat for its profile background. + + .. versionadded:: 20.8 + emoji_status_custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of emoji + status of the chat or the other party in a private chat. + + .. versionadded:: 20.0 + emoji_status_expiration_date (:class:`datetime.datetime`, optional): Expiration date of + emoji status of the chat or the other party in a private chat, in seconds. + + |datetime_localization| + + .. versionadded:: 20.5 + bio (:obj:`str`, optional): Bio of the other party in a private chat. + has_private_forwards (:obj:`bool`, optional): :obj:`True`, if privacy settings of the other + party in the private chat allows to use ``tg://user?id=`` links only in chats + with the user. + + .. versionadded:: 13.9 + has_restricted_voice_and_video_messages (:obj:`bool`, optional): :obj:`True`, if the + privacy settings of the other party restrict sending voice and video note messages + in the private chat. + + .. versionadded:: 20.0 + join_to_send_messages (:obj:`bool`, optional): :obj:`True`, if users need to join the + supergroup before they can send messages. + + .. versionadded:: 20.0 + join_by_request (:obj:`bool`, optional): :obj:`True`, if all users directly joining the + supergroup without using an invite link need to be approved by supergroup + administrators. + + .. versionadded:: 20.0 + description (:obj:`str`, optional): Description, for groups, supergroups and channel chats. + invite_link (:obj:`str`, optional): Primary invite link, for groups, supergroups and + channel. + pinned_message (:class:`telegram.Message`, optional): The most recent pinned message + (by sending date). + permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions, + for groups and supergroups. + slow_mode_delay (:obj:`int`, optional): For supergroups, the minimum allowed delay between + consecutive messages sent by each unprivileged user. + unrestrict_boost_count (:obj:`int`, optional): For supergroups, the minimum number of + boosts that a non-administrator user needs to add in order to ignore slow mode and chat + permissions. + + .. versionadded:: 21.0 + message_auto_delete_time (:obj:`int`, optional): The time after which all messages sent to + the chat will be automatically deleted; in seconds. + + .. versionadded:: 13.4 + has_aggressive_anti_spam_enabled (:obj:`bool`, optional): :obj:`True`, if aggressive + anti-spam checks are enabled in the supergroup. The field is only available to chat + administrators. + + .. versionadded:: 20.0 + has_hidden_members (:obj:`bool`, optional): :obj:`True`, if non-administrators can only + get the list of bots and administrators in the chat. + + .. versionadded:: 20.0 + has_protected_content (:obj:`bool`, optional): :obj:`True`, if messages from the chat can't + be forwarded to other chats. + + .. versionadded:: 13.9 + has_visible_history (:obj:`bool`, optional): :obj:`True`, if new chat members will have + access to old messages; available only to chat administrators. + + .. versionadded:: 20.8 + sticker_set_name (:obj:`str`, optional): For supergroups, name of group sticker set. + can_set_sticker_set (:obj:`bool`, optional): :obj:`True`, if the bot can change group the + sticker set. + custom_emoji_sticker_set_name (:obj:`str`, optional): For supergroups, the name of the + group's custom emoji sticker set. Custom emoji from this set can be used by all users + and bots in the group. + + .. versionadded:: 21.0 + linked_chat_id (:obj:`int`, optional): Unique identifier for the linked chat, i.e. the + discussion group identifier for a channel and vice versa; for supergroups and channel + chats. + location (:class:`telegram.ChatLocation`, optional): For supergroups, the location to which + the supergroup is connected. Attributes: + id (:obj:`int`): Unique identifier for this chat. + type (:obj:`str`): Type of chat, can be either :attr:`PRIVATE`, :attr:`GROUP`, + :attr:`SUPERGROUP` or :attr:`CHANNEL`. + accent_color_id (:obj:`int`): Optional. Identifier of the + :class:`accent color ` for the chat name and + backgrounds of the chat photo, reply header, and link preview. See `accent colors`_ + for more details. + + .. versionadded:: 20.8 max_reaction_count (:obj:`int`): The maximum number of reactions that can be set on a message in the chat. .. versionadded:: 21.2 + title (:obj:`str`, optional): Title, for supergroups, channels and group chats. + username (:obj:`str`, optional): Username, for private chats, supergroups and channels if + available. + first_name (:obj:`str`, optional): First name of the other party in a private chat. + last_name (:obj:`str`, optional): Last name of the other party in a private chat. + is_forum (:obj:`bool`, optional): :obj:`True`, if the supergroup chat is a forum + (has topics_ enabled). + + .. versionadded:: 20.0 + photo (:class:`telegram.ChatPhoto`): Optional. Chat photo. + active_usernames (Tuple[:obj:`str`]): Optional. If set, the list of all `active chat + usernames `_; for private chats, supergroups and channels. + + This list is empty if the chat has no active usernames or this chat instance was not + obtained via :meth:`~telegram.Bot.get_chat`. + + .. versionadded:: 20.0 + birthdate (:obj:`telegram.Birthdate`): Optional. For private chats, + the date of birth of the user. + + .. versionadded:: 21.1 + business_intro (:class:`telegram.BusinessIntro`): Optional. For private chats with + business accounts, the intro of the business. + + .. versionadded:: 21.1 + business_location (:class:`telegram.BusinessLocation`): Optional. For private chats with + business accounts, the location of the business. + + .. versionadded:: 21.1 + business_opening_hours (:class:`telegram.BusinessOpeningHours`): Optional. For private + chats with business accounts, the opening hours of the business. + + .. versionadded:: 21.1 + personal_chat (:obj:`telegram.Chat`): Optional. For private chats, the personal channel of + the user. + + .. versionadded:: 21.1 + available_reactions (Tuple[:class:`telegram.ReactionType`]): Optional. List of available + reactions allowed in the chat. If omitted, then all of + :const:`telegram.constants.ReactionEmoji` are allowed. + + .. versionadded:: 20.8 + background_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of emoji chosen + by the chat for the reply header and link preview background. + + .. versionadded:: 20.8 + profile_accent_color_id (:obj:`int`): Optional. Identifier of the + :class:`accent color ` for the chat's profile + background. See profile `accent colors`_ for more details. + + .. versionadded:: 20.8 + profile_background_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of + the emoji chosen by the chat for its profile background. + + .. versionadded:: 20.8 + emoji_status_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of emoji + status of the chat or the other party in a private chat. + + .. versionadded:: 20.0 + emoji_status_expiration_date (:class:`datetime.datetime`): Optional. Expiration date of + emoji status of the chat or the other party in a private chat, in seconds. + + |datetime_localization| + + .. versionadded:: 20.5 + bio (:obj:`str`): Optional. Bio of the other party in a private chat. + has_private_forwards (:obj:`bool`): Optional. :obj:`True`, if privacy settings of the other + party in the private chat allows to use ``tg://user?id=`` links only in chats + with the user. + + .. versionadded:: 13.9 + has_restricted_voice_and_video_messages (:obj:`bool`): Optional. :obj:`True`, if the + privacy settings of the other party restrict sending voice and video note messages + in the private chat. + + .. versionadded:: 20.0 + join_to_send_messages (:obj:`bool`): Optional. :obj:`True`, if users need to join + the supergroup before they can send messages. + + .. versionadded:: 20.0 + join_by_request (:obj:`bool`): Optional. :obj:`True`, if all users directly joining the + supergroup without using an invite link need to be approved by supergroup + administrators. + + .. versionadded:: 20.0 + description (:obj:`str`): Optional. Description, for groups, supergroups and channel chats. + invite_link (:obj:`str`): Optional. Primary invite link, for groups, supergroups and + channel. + pinned_message (:class:`telegram.Message`): Optional. The most recent pinned message + (by sending date). + permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions, + for groups and supergroups. + slow_mode_delay (:obj:`int`): Optional. For supergroups, the minimum allowed delay between + consecutive messages sent by each unprivileged user. + unrestrict_boost_count (:obj:`int`): Optional. For supergroups, the minimum number of + boosts that a non-administrator user needs to add in order to ignore slow mode and chat + permissions. + + .. versionadded:: 21.0 + message_auto_delete_time (:obj:`int`): Optional. The time after which all messages sent to + the chat will be automatically deleted; in seconds. + + .. versionadded:: 13.4 + has_aggressive_anti_spam_enabled (:obj:`bool`): Optional. :obj:`True`, if aggressive + anti-spam checks are enabled in the supergroup. The field is only available to chat + administrators. + + .. versionadded:: 20.0 + has_hidden_members (:obj:`bool`): Optional. :obj:`True`, if non-administrators can only + get the list of bots and administrators in the chat. + + .. versionadded:: 20.0 + has_protected_content (:obj:`bool`): Optional. :obj:`True`, if messages from the chat can't + be forwarded to other chats. + + .. versionadded:: 13.9 + has_visible_history (:obj:`bool`): Optional. :obj:`True`, if new chat members will have + access to old messages; available only to chat administrators. + + .. versionadded:: 20.8 + sticker_set_name (:obj:`str`): Optional. For supergroups, name of Group sticker set. + can_set_sticker_set (:obj:`bool`): Optional. :obj:`True`, if the bot can change group the + sticker set. + custom_emoji_sticker_set_name (:obj:`str`): Optional. For supergroups, the name of the + group's custom emoji sticker set. Custom emoji from this set can be used by all users + and bots in the group. + + .. versionadded:: 21.0 + linked_chat_id (:obj:`int`): Optional. Unique identifier for the linked chat, i.e. the + discussion group identifier for a channel and vice versa; for supergroups and channel + chats. + location (:class:`telegram.ChatLocation`): Optional. For supergroups, the location to which + the supergroup is connected. + + .. _accent colors: https://core.telegram.org/bots/api#accent-colors + .. _topics: https://telegram.org/blog/topics-in-groups-collectible-usernames#topics-in-groups """ - __slots__ = ("max_reaction_count",) + __slots__ = ( + "accent_color_id", + "active_usernames", + "available_reactions", + "background_custom_emoji_id", + "bio", + "birthdate", + "business_intro", + "business_location", + "business_opening_hours", + "can_set_sticker_set", + "custom_emoji_sticker_set_name", + "description", + "emoji_status_custom_emoji_id", + "emoji_status_expiration_date", + "has_aggressive_anti_spam_enabled", + "has_hidden_members", + "has_private_forwards", + "has_protected_content", + "has_restricted_voice_and_video_messages", + "has_visible_history", + "invite_link", + "join_by_request", + "join_to_send_messages", + "linked_chat_id", + "location", + "max_reaction_count", + "message_auto_delete_time", + "permissions", + "personal_chat", + "photo", + "pinned_message", + "profile_accent_color_id", + "profile_background_custom_emoji_id", + "slow_mode_delay", + "sticker_set_name", + "unrestrict_boost_count", + ) def __init__( self, id: int, type: str, - accent_color_id: int, # API 7.3 made this argument required - max_reaction_count: int, # NEW arg in api 7.3 and is required + accent_color_id: int, + max_reaction_count: int, title: Optional[str] = None, username: Optional[str] = None, first_name: Optional[str] = None, @@ -120,47 +442,93 @@ def __init__( username=username, first_name=first_name, last_name=last_name, - photo=photo, - description=description, - invite_link=invite_link, - pinned_message=pinned_message, - permissions=permissions, - sticker_set_name=sticker_set_name, - can_set_sticker_set=can_set_sticker_set, - slow_mode_delay=slow_mode_delay, - bio=bio, - linked_chat_id=linked_chat_id, - location=location, - message_auto_delete_time=message_auto_delete_time, - has_private_forwards=has_private_forwards, - has_protected_content=has_protected_content, - join_to_send_messages=join_to_send_messages, - join_by_request=join_by_request, - has_restricted_voice_and_video_messages=has_restricted_voice_and_video_messages, is_forum=is_forum, - active_usernames=active_usernames, - emoji_status_custom_emoji_id=emoji_status_custom_emoji_id, - emoji_status_expiration_date=emoji_status_expiration_date, - has_aggressive_anti_spam_enabled=has_aggressive_anti_spam_enabled, - has_hidden_members=has_hidden_members, - available_reactions=available_reactions, - accent_color_id=accent_color_id, - background_custom_emoji_id=background_custom_emoji_id, - profile_accent_color_id=profile_accent_color_id, - profile_background_custom_emoji_id=profile_background_custom_emoji_id, - has_visible_history=has_visible_history, - unrestrict_boost_count=unrestrict_boost_count, - custom_emoji_sticker_set_name=custom_emoji_sticker_set_name, - birthdate=birthdate, - personal_chat=personal_chat, - business_intro=business_intro, - business_location=business_location, - business_opening_hours=business_opening_hours, api_kwargs=api_kwargs, ) # Required and unique to this class- with self._unfrozen(): self.max_reaction_count: int = max_reaction_count + self.photo: Optional[ChatPhoto] = photo + self.bio: Optional[str] = bio + self.has_private_forwards: Optional[bool] = has_private_forwards + self.description: Optional[str] = description + self.invite_link: Optional[str] = invite_link + self.pinned_message: Optional[Message] = pinned_message + self.permissions: Optional[ChatPermissions] = permissions + self.slow_mode_delay: Optional[int] = slow_mode_delay + self.message_auto_delete_time: Optional[int] = ( + int(message_auto_delete_time) if message_auto_delete_time is not None else None + ) + self.has_protected_content: Optional[bool] = has_protected_content + self.has_visible_history: Optional[bool] = has_visible_history + self.sticker_set_name: Optional[str] = sticker_set_name + self.can_set_sticker_set: Optional[bool] = can_set_sticker_set + self.linked_chat_id: Optional[int] = linked_chat_id + self.location: Optional[ChatLocation] = location + self.join_to_send_messages: Optional[bool] = join_to_send_messages + self.join_by_request: Optional[bool] = join_by_request + self.has_restricted_voice_and_video_messages: Optional[bool] = ( + has_restricted_voice_and_video_messages + ) + self.active_usernames: Tuple[str, ...] = parse_sequence_arg(active_usernames) + self.emoji_status_custom_emoji_id: Optional[str] = emoji_status_custom_emoji_id + self.emoji_status_expiration_date: Optional[datetime] = emoji_status_expiration_date + self.has_aggressive_anti_spam_enabled: Optional[bool] = ( + has_aggressive_anti_spam_enabled + ) + self.has_hidden_members: Optional[bool] = has_hidden_members + self.available_reactions: Optional[Tuple[ReactionType, ...]] = parse_sequence_arg( + available_reactions + ) + self.accent_color_id: Optional[int] = accent_color_id + self.background_custom_emoji_id: Optional[str] = background_custom_emoji_id + self.profile_accent_color_id: Optional[int] = profile_accent_color_id + self.profile_background_custom_emoji_id: Optional[str] = ( + profile_background_custom_emoji_id + ) + self.unrestrict_boost_count: Optional[int] = unrestrict_boost_count + self.custom_emoji_sticker_set_name: Optional[str] = custom_emoji_sticker_set_name + self.birthdate: Optional[Birthdate] = birthdate + self.personal_chat: Optional["Chat"] = personal_chat + self.business_intro: Optional["BusinessIntro"] = business_intro + self.business_location: Optional["BusinessLocation"] = business_location + self.business_opening_hours: Optional["BusinessOpeningHours"] = business_opening_hours + + @classmethod + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatFullInfo"]: + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) + + if not data: + return None + + # Get the local timezone from the bot if it has defaults + loc_tzinfo = extract_tzinfo_from_defaults(bot) + + data["emoji_status_expiration_date"] = from_timestamp( + data.get("emoji_status_expiration_date"), tzinfo=loc_tzinfo + ) + + data["photo"] = ChatPhoto.de_json(data.get("photo"), bot) + + from telegram import ( # pylint: disable=import-outside-toplevel + BusinessIntro, + BusinessLocation, + BusinessOpeningHours, + Message, + ) + + data["pinned_message"] = Message.de_json(data.get("pinned_message"), bot) + data["permissions"] = ChatPermissions.de_json(data.get("permissions"), bot) + data["location"] = ChatLocation.de_json(data.get("location"), bot) + data["available_reactions"] = ReactionType.de_list(data.get("available_reactions"), bot) + data["birthdate"] = Birthdate.de_json(data.get("birthdate"), bot) + data["personal_chat"] = Chat.de_json(data.get("personal_chat"), bot) + data["business_intro"] = BusinessIntro.de_json(data.get("business_intro"), bot) + data["business_location"] = BusinessLocation.de_json(data.get("business_location"), bot) + data["business_opening_hours"] = BusinessOpeningHours.de_json( + data.get("business_opening_hours"), bot + ) - self._freeze() + return super().de_json(data=data, bot=bot) diff --git a/telegram/_message.py b/telegram/_message.py index eaea1f3fb08..195724d36f7 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -3331,7 +3331,7 @@ async def forward( Note: Since the release of Bot API 5.5 it can be impossible to forward messages from some chats. Use the attributes :attr:`telegram.Message.has_protected_content` and - :attr:`telegram.Chat.has_protected_content` to check this. + :attr:`telegram.ChatFullInfo.has_protected_content` to check this. As a workaround, it is still possible to use :meth:`copy`. However, this behaviour is undocumented and might be changed by Telegram. diff --git a/telegram/constants.py b/telegram/constants.py index a2d91885cb4..06f5bff86f5 100644 --- a/telegram/constants.py +++ b/telegram/constants.py @@ -168,7 +168,8 @@ class _AccentColor(NamedTuple): class AccentColor(Enum): - """This enum contains the available accent colors for :class:`telegram.Chat.accent_color_id`. + """This enum contains the available accent colors for + :class:`telegram.ChatFullInfo.accent_color_id`. The members of this enum are named tuples with the following attributes: - ``identifier`` (:obj:`int`): The identifier of the accent color. @@ -1959,7 +1960,7 @@ class PollingLimit(IntEnum): class ProfileAccentColor(Enum): """This enum contains the available accent colors for - :class:`telegram.Chat.profile_accent_color_id`. + :class:`telegram.ChatFullInfo.profile_accent_color_id`. The members of this enum are named tuples with the following attributes: - ``identifier`` (:obj:`int`): The identifier of the accent color. diff --git a/tests/test_chat.py b/tests/test_chat.py index 7af7a677ce0..36c1e80a800 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -17,31 +17,12 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. -import datetime -import warnings import pytest -from telegram import ( - Birthdate, - Bot, - BusinessIntro, - BusinessLocation, - BusinessOpeningHours, - BusinessOpeningHoursInterval, - Chat, - ChatLocation, - ChatPermissions, - Location, - ReactionTypeCustomEmoji, - ReactionTypeEmoji, - User, -) -from telegram._chat import _deprecated_attrs -from telegram._utils.datetime import UTC, to_timestamp +from telegram import Bot, Chat, ChatPermissions, ReactionTypeEmoji, User from telegram.constants import ChatAction, ChatType, ReactionEmoji from telegram.helpers import escape_markdown -from telegram.warnings import PTBDeprecationWarning from tests.auxil.bot_method_checks import ( check_defaults_handling, check_shortcut_call, @@ -57,37 +38,7 @@ def chat(bot): title=TestChatBase.title, type=TestChatBase.type_, username=TestChatBase.username, - sticker_set_name=TestChatBase.sticker_set_name, - can_set_sticker_set=TestChatBase.can_set_sticker_set, - permissions=TestChatBase.permissions, - slow_mode_delay=TestChatBase.slow_mode_delay, - bio=TestChatBase.bio, - linked_chat_id=TestChatBase.linked_chat_id, - location=TestChatBase.location, - has_private_forwards=True, - has_protected_content=True, - has_visible_history=True, - join_to_send_messages=True, - join_by_request=True, - has_restricted_voice_and_video_messages=True, is_forum=True, - active_usernames=TestChatBase.active_usernames, - emoji_status_custom_emoji_id=TestChatBase.emoji_status_custom_emoji_id, - emoji_status_expiration_date=TestChatBase.emoji_status_expiration_date, - has_aggressive_anti_spam_enabled=TestChatBase.has_aggressive_anti_spam_enabled, - has_hidden_members=TestChatBase.has_hidden_members, - available_reactions=TestChatBase.available_reactions, - accent_color_id=TestChatBase.accent_color_id, - background_custom_emoji_id=TestChatBase.background_custom_emoji_id, - profile_accent_color_id=TestChatBase.profile_accent_color_id, - profile_background_custom_emoji_id=TestChatBase.profile_background_custom_emoji_id, - unrestrict_boost_count=TestChatBase.unrestrict_boost_count, - custom_emoji_sticker_set_name=TestChatBase.custom_emoji_sticker_set_name, - business_intro=TestChatBase.business_intro, - business_location=TestChatBase.business_location, - business_opening_hours=TestChatBase.business_opening_hours, - birthdate=Birthdate(1, 1), - personal_chat=TestChatBase.personal_chat, first_name=TestChatBase.first_name, last_name=TestChatBase.last_name, ) @@ -101,48 +52,7 @@ class TestChatBase: title = "ToledosPalaceBot - Group" type_ = "group" username = "username" - all_members_are_administrators = False - sticker_set_name = "stickers" - can_set_sticker_set = False - permissions = ChatPermissions( - can_send_messages=True, - can_change_info=False, - can_invite_users=True, - ) - slow_mode_delay = 30 - bio = "I'm a Barbie Girl in a Barbie World" - linked_chat_id = 11880 - location = ChatLocation(Location(123, 456), "Barbie World") - has_protected_content = True - has_visible_history = True - has_private_forwards = True - join_to_send_messages = True - join_by_request = True - has_restricted_voice_and_video_messages = True is_forum = True - active_usernames = ["These", "Are", "Usernames!"] - emoji_status_custom_emoji_id = "VeryUniqueCustomEmojiID" - emoji_status_expiration_date = datetime.datetime.now(tz=UTC).replace(microsecond=0) - has_aggressive_anti_spam_enabled = True - has_hidden_members = True - available_reactions = [ - ReactionTypeEmoji(ReactionEmoji.THUMBS_DOWN), - ReactionTypeCustomEmoji("custom_emoji_id"), - ] - business_intro = BusinessIntro("Title", "Description", None) - business_location = BusinessLocation("Address", Location(123, 456)) - business_opening_hours = BusinessOpeningHours( - "Country/City", - [BusinessOpeningHoursInterval(opening, opening + 60) for opening in (0, 24 * 60)], - ) - accent_color_id = 1 - background_custom_emoji_id = "background_custom_emoji_id" - profile_accent_color_id = 2 - profile_background_custom_emoji_id = "profile_background_custom_emoji_id" - unrestrict_boost_count = 100 - custom_emoji_sticker_set_name = "custom_emoji_sticker_set_name" - birthdate = Birthdate(1, 1) - personal_chat = Chat(3, "private", "private") first_name = "first" last_name = "last" @@ -159,40 +69,7 @@ def test_de_json(self, bot): "title": self.title, "type": self.type_, "username": self.username, - "all_members_are_administrators": self.all_members_are_administrators, - "sticker_set_name": self.sticker_set_name, - "can_set_sticker_set": self.can_set_sticker_set, - "permissions": self.permissions.to_dict(), - "slow_mode_delay": self.slow_mode_delay, - "bio": self.bio, - "business_intro": self.business_intro.to_dict(), - "business_location": self.business_location.to_dict(), - "business_opening_hours": self.business_opening_hours.to_dict(), - "has_protected_content": self.has_protected_content, - "has_visible_history": self.has_visible_history, - "has_private_forwards": self.has_private_forwards, - "linked_chat_id": self.linked_chat_id, - "location": self.location.to_dict(), - "join_to_send_messages": self.join_to_send_messages, - "join_by_request": self.join_by_request, - "has_restricted_voice_and_video_messages": ( - self.has_restricted_voice_and_video_messages - ), "is_forum": self.is_forum, - "active_usernames": self.active_usernames, - "emoji_status_custom_emoji_id": self.emoji_status_custom_emoji_id, - "emoji_status_expiration_date": to_timestamp(self.emoji_status_expiration_date), - "has_aggressive_anti_spam_enabled": self.has_aggressive_anti_spam_enabled, - "has_hidden_members": self.has_hidden_members, - "available_reactions": [reaction.to_dict() for reaction in self.available_reactions], - "accent_color_id": self.accent_color_id, - "background_custom_emoji_id": self.background_custom_emoji_id, - "profile_accent_color_id": self.profile_accent_color_id, - "profile_background_custom_emoji_id": self.profile_background_custom_emoji_id, - "unrestrict_boost_count": self.unrestrict_boost_count, - "custom_emoji_sticker_set_name": self.custom_emoji_sticker_set_name, - "birthdate": self.birthdate.to_dict(), - "personal_chat": self.personal_chat.to_dict(), "first_name": self.first_name, "last_name": self.last_name, } @@ -202,76 +79,10 @@ def test_de_json(self, bot): assert chat.title == self.title assert chat.type == self.type_ assert chat.username == self.username - assert chat.sticker_set_name == self.sticker_set_name - assert chat.can_set_sticker_set == self.can_set_sticker_set - assert chat.permissions == self.permissions - assert chat.slow_mode_delay == self.slow_mode_delay - assert chat.bio == self.bio - assert chat.business_intro == self.business_intro - assert chat.business_location == self.business_location - assert chat.business_opening_hours == self.business_opening_hours - assert chat.has_protected_content == self.has_protected_content - assert chat.has_visible_history == self.has_visible_history - assert chat.has_private_forwards == self.has_private_forwards - assert chat.linked_chat_id == self.linked_chat_id - assert chat.location.location == self.location.location - assert chat.location.address == self.location.address - assert chat.join_to_send_messages == self.join_to_send_messages - assert chat.join_by_request == self.join_by_request - assert ( - chat.has_restricted_voice_and_video_messages - == self.has_restricted_voice_and_video_messages - ) - assert chat.api_kwargs == { - "all_members_are_administrators": self.all_members_are_administrators - } assert chat.is_forum == self.is_forum - assert chat.active_usernames == tuple(self.active_usernames) - assert chat.emoji_status_custom_emoji_id == self.emoji_status_custom_emoji_id - assert chat.emoji_status_expiration_date == (self.emoji_status_expiration_date) - assert chat.has_aggressive_anti_spam_enabled == self.has_aggressive_anti_spam_enabled - assert chat.has_hidden_members == self.has_hidden_members - assert chat.available_reactions == tuple(self.available_reactions) - assert chat.accent_color_id == self.accent_color_id - assert chat.background_custom_emoji_id == self.background_custom_emoji_id - assert chat.profile_accent_color_id == self.profile_accent_color_id - assert chat.profile_background_custom_emoji_id == self.profile_background_custom_emoji_id - assert chat.unrestrict_boost_count == self.unrestrict_boost_count - assert chat.custom_emoji_sticker_set_name == self.custom_emoji_sticker_set_name - assert chat.birthdate == self.birthdate - assert chat.personal_chat == self.personal_chat assert chat.first_name == self.first_name assert chat.last_name == self.last_name - def test_de_json_localization(self, bot, raw_bot, tz_bot): - json_dict = { - "id": self.id_, - "type": self.type_, - "emoji_status_expiration_date": to_timestamp(self.emoji_status_expiration_date), - } - chat_bot = Chat.de_json(json_dict, bot) - chat_bot_raw = Chat.de_json(json_dict, raw_bot) - chat_bot_tz = Chat.de_json(json_dict, tz_bot) - - # comparing utcoffsets because comparing tzinfo objects is not reliable - emoji_expire_offset = chat_bot_tz.emoji_status_expiration_date.utcoffset() - emoji_expire_offset_tz = tz_bot.defaults.tzinfo.utcoffset( - chat_bot_tz.emoji_status_expiration_date.replace(tzinfo=None) - ) - - assert chat_bot.emoji_status_expiration_date.tzinfo == UTC - assert chat_bot_raw.emoji_status_expiration_date.tzinfo == UTC - assert emoji_expire_offset_tz == emoji_expire_offset - - def test_always_tuples_attributes(self): - chat = Chat( - id=123, - title="title", - type=Chat.PRIVATE, - ) - assert isinstance(chat.active_usernames, tuple) - assert chat.active_usernames == () - def test_to_dict(self, chat): chat_dict = chat.to_dict() @@ -280,67 +91,10 @@ def test_to_dict(self, chat): assert chat_dict["title"] == chat.title assert chat_dict["type"] == chat.type assert chat_dict["username"] == chat.username - assert chat_dict["permissions"] == chat.permissions.to_dict() - assert chat_dict["slow_mode_delay"] == chat.slow_mode_delay - assert chat_dict["bio"] == chat.bio - assert chat_dict["business_intro"] == chat.business_intro.to_dict() - assert chat_dict["business_location"] == chat.business_location.to_dict() - assert chat_dict["business_opening_hours"] == chat.business_opening_hours.to_dict() - assert chat_dict["has_private_forwards"] == chat.has_private_forwards - assert chat_dict["has_protected_content"] == chat.has_protected_content - assert chat_dict["has_visible_history"] == chat.has_visible_history - assert chat_dict["linked_chat_id"] == chat.linked_chat_id - assert chat_dict["location"] == chat.location.to_dict() - assert chat_dict["join_to_send_messages"] == chat.join_to_send_messages - assert chat_dict["join_by_request"] == chat.join_by_request - assert ( - chat_dict["has_restricted_voice_and_video_messages"] - == chat.has_restricted_voice_and_video_messages - ) assert chat_dict["is_forum"] == chat.is_forum - assert chat_dict["active_usernames"] == list(chat.active_usernames) - assert chat_dict["emoji_status_custom_emoji_id"] == chat.emoji_status_custom_emoji_id - assert chat_dict["emoji_status_expiration_date"] == to_timestamp( - chat.emoji_status_expiration_date - ) - assert ( - chat_dict["has_aggressive_anti_spam_enabled"] == chat.has_aggressive_anti_spam_enabled - ) - assert chat_dict["has_hidden_members"] == chat.has_hidden_members - assert chat_dict["available_reactions"] == [ - reaction.to_dict() for reaction in chat.available_reactions - ] - assert chat_dict["accent_color_id"] == chat.accent_color_id - assert chat_dict["background_custom_emoji_id"] == chat.background_custom_emoji_id - assert chat_dict["profile_accent_color_id"] == chat.profile_accent_color_id - assert ( - chat_dict["profile_background_custom_emoji_id"] - == chat.profile_background_custom_emoji_id - ) - assert chat_dict["custom_emoji_sticker_set_name"] == chat.custom_emoji_sticker_set_name - assert chat_dict["unrestrict_boost_count"] == chat.unrestrict_boost_count - assert chat_dict["birthdate"] == chat.birthdate.to_dict() - assert chat_dict["personal_chat"] == chat.personal_chat.to_dict() assert chat_dict["first_name"] == chat.first_name assert chat_dict["last_name"] == chat.last_name - def test_deprecated_attributes(self, chat): - for depr_attr in _deprecated_attrs: - with pytest.warns(PTBDeprecationWarning, match="deprecated and will only be accessib"): - getattr(chat, depr_attr) - with warnings.catch_warnings(): # No warning should be raised - warnings.simplefilter("error") - chat.id - chat.first_name - - def test_deprecated_arguments(self): - for depr_attr in _deprecated_attrs: - with pytest.warns(PTBDeprecationWarning, match="deprecated and will only be availabl"): - Chat(1, "type", **{depr_attr: "1"}) - with warnings.catch_warnings(): # No warning should be raised - warnings.simplefilter("error") - Chat(1, "type", first_name="first_name") - def test_enum_init(self): chat = Chat(id=1, type="foo") assert chat.type == "foo" diff --git a/tests/test_chatfullinfo.py b/tests/test_chatfullinfo.py index f42642e4ed2..b547e4de913 100644 --- a/tests/test_chatfullinfo.py +++ b/tests/test_chatfullinfo.py @@ -17,7 +17,6 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. import datetime -import warnings import pytest @@ -35,7 +34,6 @@ ReactionTypeCustomEmoji, ReactionTypeEmoji, ) -from telegram._chat import _deprecated_attrs from telegram._utils.datetime import UTC, to_timestamp from telegram.constants import ReactionEmoji from tests.auxil.slots import mro_slots @@ -44,19 +42,19 @@ @pytest.fixture(scope="module") def chat_full_info(bot): chat = ChatFullInfo( - TestChatInfoBase.id_, - type=TestChatInfoBase.type_, - accent_color_id=TestChatInfoBase.accent_color_id, - max_reaction_count=TestChatInfoBase.max_reaction_count, - title=TestChatInfoBase.title, - username=TestChatInfoBase.username, - sticker_set_name=TestChatInfoBase.sticker_set_name, - can_set_sticker_set=TestChatInfoBase.can_set_sticker_set, - permissions=TestChatInfoBase.permissions, - slow_mode_delay=TestChatInfoBase.slow_mode_delay, - bio=TestChatInfoBase.bio, - linked_chat_id=TestChatInfoBase.linked_chat_id, - location=TestChatInfoBase.location, + TestChatFullInfoBase.id_, + type=TestChatFullInfoBase.type_, + accent_color_id=TestChatFullInfoBase.accent_color_id, + max_reaction_count=TestChatFullInfoBase.max_reaction_count, + title=TestChatFullInfoBase.title, + username=TestChatFullInfoBase.username, + sticker_set_name=TestChatFullInfoBase.sticker_set_name, + can_set_sticker_set=TestChatFullInfoBase.can_set_sticker_set, + permissions=TestChatFullInfoBase.permissions, + slow_mode_delay=TestChatFullInfoBase.slow_mode_delay, + bio=TestChatFullInfoBase.bio, + linked_chat_id=TestChatFullInfoBase.linked_chat_id, + location=TestChatFullInfoBase.location, has_private_forwards=True, has_protected_content=True, has_visible_history=True, @@ -64,35 +62,37 @@ def chat_full_info(bot): join_by_request=True, has_restricted_voice_and_video_messages=True, is_forum=True, - active_usernames=TestChatInfoBase.active_usernames, - emoji_status_custom_emoji_id=TestChatInfoBase.emoji_status_custom_emoji_id, - emoji_status_expiration_date=TestChatInfoBase.emoji_status_expiration_date, - has_aggressive_anti_spam_enabled=TestChatInfoBase.has_aggressive_anti_spam_enabled, - has_hidden_members=TestChatInfoBase.has_hidden_members, - available_reactions=TestChatInfoBase.available_reactions, - background_custom_emoji_id=TestChatInfoBase.background_custom_emoji_id, - profile_accent_color_id=TestChatInfoBase.profile_accent_color_id, - profile_background_custom_emoji_id=TestChatInfoBase.profile_background_custom_emoji_id, - unrestrict_boost_count=TestChatInfoBase.unrestrict_boost_count, - custom_emoji_sticker_set_name=TestChatInfoBase.custom_emoji_sticker_set_name, - business_intro=TestChatInfoBase.business_intro, - business_location=TestChatInfoBase.business_location, - business_opening_hours=TestChatInfoBase.business_opening_hours, + active_usernames=TestChatFullInfoBase.active_usernames, + emoji_status_custom_emoji_id=TestChatFullInfoBase.emoji_status_custom_emoji_id, + emoji_status_expiration_date=TestChatFullInfoBase.emoji_status_expiration_date, + has_aggressive_anti_spam_enabled=TestChatFullInfoBase.has_aggressive_anti_spam_enabled, + has_hidden_members=TestChatFullInfoBase.has_hidden_members, + available_reactions=TestChatFullInfoBase.available_reactions, + background_custom_emoji_id=TestChatFullInfoBase.background_custom_emoji_id, + profile_accent_color_id=TestChatFullInfoBase.profile_accent_color_id, + profile_background_custom_emoji_id=TestChatFullInfoBase.profile_background_custom_emoji_id, + unrestrict_boost_count=TestChatFullInfoBase.unrestrict_boost_count, + custom_emoji_sticker_set_name=TestChatFullInfoBase.custom_emoji_sticker_set_name, + business_intro=TestChatFullInfoBase.business_intro, + business_location=TestChatFullInfoBase.business_location, + business_opening_hours=TestChatFullInfoBase.business_opening_hours, birthdate=Birthdate(1, 1), - personal_chat=TestChatInfoBase.personal_chat, + personal_chat=TestChatFullInfoBase.personal_chat, + first_name="first_name", + last_name="last_name", ) chat.set_bot(bot) chat._unfreeze() return chat -class TestChatInfoBase: +# Shortcut methods are tested in test_chat.py. +class TestChatFullInfoBase: id_ = -28767330 max_reaction_count = 2 title = "ToledosPalaceBot - Group" type_ = "group" username = "username" - all_members_are_administrators = False sticker_set_name = "stickers" can_set_sticker_set = False permissions = ChatPermissions( @@ -134,13 +134,16 @@ class TestChatInfoBase: custom_emoji_sticker_set_name = "custom_emoji_sticker_set_name" birthdate = Birthdate(1, 1) personal_chat = Chat(3, "private", "private") + first_name = "first_name" + last_name = "last_name" -class TestChatWithoutRequest(TestChatInfoBase): +class TestChatFullInfoWithoutRequest(TestChatFullInfoBase): def test_slot_behaviour(self, chat_full_info): cfi = chat_full_info for attr in cfi.__slots__: assert getattr(cfi, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(cfi)) == len(set(mro_slots(cfi))), "duplicate slot" def test_de_json(self, bot): @@ -151,7 +154,6 @@ def test_de_json(self, bot): "accent_color_id": self.accent_color_id, "max_reaction_count": self.max_reaction_count, "username": self.username, - "all_members_are_administrators": self.all_members_are_administrators, "sticker_set_name": self.sticker_set_name, "can_set_sticker_set": self.can_set_sticker_set, "permissions": self.permissions.to_dict(), @@ -184,26 +186,134 @@ def test_de_json(self, bot): "custom_emoji_sticker_set_name": self.custom_emoji_sticker_set_name, "birthdate": self.birthdate.to_dict(), "personal_chat": self.personal_chat.to_dict(), + "first_name": self.first_name, + "last_name": self.last_name, } cfi = ChatFullInfo.de_json(json_dict, bot) + assert cfi.id == self.id_ + assert cfi.title == self.title + assert cfi.type == self.type_ + assert cfi.username == self.username + assert cfi.sticker_set_name == self.sticker_set_name + assert cfi.can_set_sticker_set == self.can_set_sticker_set + assert cfi.permissions == self.permissions + assert cfi.slow_mode_delay == self.slow_mode_delay + assert cfi.bio == self.bio + assert cfi.business_intro == self.business_intro + assert cfi.business_location == self.business_location + assert cfi.business_opening_hours == self.business_opening_hours + assert cfi.has_protected_content == self.has_protected_content + assert cfi.has_visible_history == self.has_visible_history + assert cfi.has_private_forwards == self.has_private_forwards + assert cfi.linked_chat_id == self.linked_chat_id + assert cfi.location.location == self.location.location + assert cfi.location.address == self.location.address + assert cfi.join_to_send_messages == self.join_to_send_messages + assert cfi.join_by_request == self.join_by_request + assert ( + cfi.has_restricted_voice_and_video_messages + == self.has_restricted_voice_and_video_messages + ) + assert cfi.is_forum == self.is_forum + assert cfi.active_usernames == tuple(self.active_usernames) + assert cfi.emoji_status_custom_emoji_id == self.emoji_status_custom_emoji_id + assert cfi.emoji_status_expiration_date == (self.emoji_status_expiration_date) + assert cfi.has_aggressive_anti_spam_enabled == self.has_aggressive_anti_spam_enabled + assert cfi.has_hidden_members == self.has_hidden_members + assert cfi.available_reactions == tuple(self.available_reactions) + assert cfi.accent_color_id == self.accent_color_id + assert cfi.background_custom_emoji_id == self.background_custom_emoji_id + assert cfi.profile_accent_color_id == self.profile_accent_color_id + assert cfi.profile_background_custom_emoji_id == self.profile_background_custom_emoji_id + assert cfi.unrestrict_boost_count == self.unrestrict_boost_count + assert cfi.custom_emoji_sticker_set_name == self.custom_emoji_sticker_set_name + assert cfi.birthdate == self.birthdate + assert cfi.personal_chat == self.personal_chat + assert cfi.first_name == self.first_name + assert cfi.last_name == self.last_name assert cfi.max_reaction_count == self.max_reaction_count + def test_de_json_localization(self, bot, raw_bot, tz_bot): + json_dict = { + "id": self.id_, + "type": self.type_, + "accent_color_id": self.accent_color_id, + "max_reaction_count": self.max_reaction_count, + "emoji_status_expiration_date": to_timestamp(self.emoji_status_expiration_date), + } + cfi_bot = ChatFullInfo.de_json(json_dict, bot) + cfi_bot_raw = ChatFullInfo.de_json(json_dict, raw_bot) + cfi_bot_tz = ChatFullInfo.de_json(json_dict, tz_bot) + + # comparing utcoffsets because comparing tzinfo objects is not reliable + emoji_expire_offset = cfi_bot_tz.emoji_status_expiration_date.utcoffset() + emoji_expire_offset_tz = tz_bot.defaults.tzinfo.utcoffset( + cfi_bot_tz.emoji_status_expiration_date.replace(tzinfo=None) + ) + + assert cfi_bot.emoji_status_expiration_date.tzinfo == UTC + assert cfi_bot_raw.emoji_status_expiration_date.tzinfo == UTC + assert emoji_expire_offset_tz == emoji_expire_offset + def test_to_dict(self, chat_full_info): cfi = chat_full_info cfi_dict = cfi.to_dict() assert isinstance(cfi_dict, dict) - assert cfi_dict["max_reaction_count"] == cfi.max_reaction_count + assert cfi_dict["id"] == cfi.id + assert cfi_dict["title"] == cfi.title + assert cfi_dict["type"] == cfi.type + assert cfi_dict["username"] == cfi.username + assert cfi_dict["permissions"] == cfi.permissions.to_dict() + assert cfi_dict["slow_mode_delay"] == cfi.slow_mode_delay + assert cfi_dict["bio"] == cfi.bio + assert cfi_dict["business_intro"] == cfi.business_intro.to_dict() + assert cfi_dict["business_location"] == cfi.business_location.to_dict() + assert cfi_dict["business_opening_hours"] == cfi.business_opening_hours.to_dict() + assert cfi_dict["has_private_forwards"] == cfi.has_private_forwards + assert cfi_dict["has_protected_content"] == cfi.has_protected_content + assert cfi_dict["has_visible_history"] == cfi.has_visible_history + assert cfi_dict["linked_chat_id"] == cfi.linked_chat_id + assert cfi_dict["location"] == cfi.location.to_dict() + assert cfi_dict["join_to_send_messages"] == cfi.join_to_send_messages + assert cfi_dict["join_by_request"] == cfi.join_by_request + assert ( + cfi_dict["has_restricted_voice_and_video_messages"] + == cfi.has_restricted_voice_and_video_messages + ) + assert cfi_dict["is_forum"] == cfi.is_forum + assert cfi_dict["active_usernames"] == list(cfi.active_usernames) + assert cfi_dict["emoji_status_custom_emoji_id"] == cfi.emoji_status_custom_emoji_id + assert cfi_dict["emoji_status_expiration_date"] == to_timestamp( + cfi.emoji_status_expiration_date + ) + assert cfi_dict["has_aggressive_anti_spam_enabled"] == cfi.has_aggressive_anti_spam_enabled + assert cfi_dict["has_hidden_members"] == cfi.has_hidden_members + assert cfi_dict["available_reactions"] == [ + reaction.to_dict() for reaction in cfi.available_reactions + ] + assert cfi_dict["accent_color_id"] == cfi.accent_color_id + assert cfi_dict["background_custom_emoji_id"] == cfi.background_custom_emoji_id + assert cfi_dict["profile_accent_color_id"] == cfi.profile_accent_color_id + assert ( + cfi_dict["profile_background_custom_emoji_id"] + == cfi.profile_background_custom_emoji_id + ) + assert cfi_dict["custom_emoji_sticker_set_name"] == cfi.custom_emoji_sticker_set_name + assert cfi_dict["unrestrict_boost_count"] == cfi.unrestrict_boost_count + assert cfi_dict["birthdate"] == cfi.birthdate.to_dict() + assert cfi_dict["personal_chat"] == cfi.personal_chat.to_dict() + assert cfi_dict["first_name"] == cfi.first_name + assert cfi_dict["last_name"] == cfi.last_name - def test_attr_access_no_warning(self, chat_full_info): - cfi = chat_full_info - for depr_attr in _deprecated_attrs: - with warnings.catch_warnings(): # No warning should be raised - warnings.simplefilter("error") - getattr(cfi, depr_attr) + assert cfi_dict["max_reaction_count"] == cfi.max_reaction_count - def test_cfi_creation_no_warning(self, chat_full_info): - cfi = chat_full_info - with warnings.catch_warnings(): - dict = cfi.to_dict() - ChatFullInfo(**dict) + def test_always_tuples_attributes(self): + cfi = ChatFullInfo( + id=123, + type=Chat.PRIVATE, + accent_color_id=1, + max_reaction_count=2, + ) + assert isinstance(cfi.active_usernames, tuple) + assert cfi.active_usernames == () diff --git a/tests/test_official/exceptions.py b/tests/test_official/exceptions.py index ac043de997d..4b44d286b32 100644 --- a/tests/test_official/exceptions.py +++ b/tests/test_official/exceptions.py @@ -20,7 +20,6 @@ from telegram import Animation, Audio, Document, PhotoSize, Sticker, Video, VideoNote, Voice -from telegram._chat import _deprecated_attrs from tests.test_official.helpers import _get_params_base IGNORED_OBJECTS = ("ResponseParameters",) @@ -173,9 +172,7 @@ def ignored_param_requirements(object_name: str) -> set[str]: # Arguments that are optional arguments for now for backwards compatibility -BACKWARDS_COMPAT_KWARGS: dict[str, set[str]] = { - "Chat": set(_deprecated_attrs), # removed by bot api 7.3 -} +BACKWARDS_COMPAT_KWARGS: dict[str, set[str]] = {} def backwards_compat_kwargs(object_name: str) -> set[str]: diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index c97db1adaea..39f3aaff4aa 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -342,10 +342,14 @@ async def test_pickle_backwards_compatibility(self): chat = (await pp.get_chat_data())[1] assert chat.id == 1 assert chat.type == Chat.PRIVATE - assert chat.api_kwargs == { + api_kwargs_expected = { "all_members_are_administrators": True, "something": "Manually inserted", } + # There are older attrs in Chat's api_kwargs which are present but we don't care about them + for k, v in api_kwargs_expected.items(): + assert chat.api_kwargs[k] == v + with pytest.raises(AttributeError): # removed attribute should not be available as attribute, only though api_kwargs chat.all_members_are_administrators From 2c299bb10974303fa46f9ea0489e5d00fab8676a Mon Sep 17 00:00:00 2001 From: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 30 May 2024 21:02:41 +0200 Subject: [PATCH 3/9] Add `setuptools` to `requirements-dev.txt` (#4282) --- requirements-dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-dev.txt b/requirements-dev.txt index 8dab397cd0b..9704921e59d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,7 @@ pre-commit # needed for pre-commit hooks in the git commit command # For the test suite +setuptools # required for test_meta pytest==8.2.0 pytest-asyncio==0.21.2 # needed because pytest doesn't come with native support for coroutines as tests pytest-xdist==3.6.1 # xdist runs tests in parallel From 57298aa0766fc3e7d6b4fffa1127548de860af8e Mon Sep 17 00:00:00 2001 From: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sat, 1 Jun 2024 21:19:38 +0200 Subject: [PATCH 4/9] Deprecate `python-telegram-bot-raw` (#4270) --- README.rst | 10 ---------- README_RAW.rst | 13 ++++++++++--- telegram/__init__.py | 27 +++++++++++++++++++++++++++ telegram/_utils/datetime.py | 2 +- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/README.rst b/README.rst index 76743bf0250..d9a61b5c95c 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,3 @@ -.. - Make sure to apply any changes to this file to README_RAW.rst as well! - .. image:: https://raw.githubusercontent.com/python-telegram-bot/logos/master/logo-text/png/ptb-logo-text_768.png :align: center :target: https://python-telegram-bot.org @@ -79,13 +76,6 @@ In addition to the pure API implementation, this library features a number of hi make the development of bots easy and straightforward. These classes are contained in the ``telegram.ext`` submodule. -A pure API implementation *without* ``telegram.ext`` is available as the standalone package ``python-telegram-bot-raw``. `See here for details. `_ - -Note ----- - -Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conjunction will result in undesired side-effects, so only install *one* of both. - Telegram API support ==================== diff --git a/README_RAW.rst b/README_RAW.rst index 45f636bc2f7..64f83121e21 100644 --- a/README_RAW.rst +++ b/README_RAW.rst @@ -1,6 +1,3 @@ -.. - Make sure to apply any changes to this file to README.rst as well! - .. image:: https://github.com/python-telegram-bot/logos/blob/master/logo-text/png/ptb-raw-logo-text_768.png?raw=true :align: center :target: https://python-telegram-bot.org @@ -62,6 +59,16 @@ :target: https://telegram.me/pythontelegrambotgroup :alt: Telegram Group +⚠️ Deprecation Notice +===================== + +The ``python-telegram-bot-raw`` library will no longer be updated after NEXT.VERSION. +Please instead use the ``python-telegram-bot`` `library `_. +The change requires no changes in your code and requires no additional dependencies. +For additional information, please see this `channel post `_. + +---- + We have made you a wrapper you can't refuse We have a vibrant community of developers helping each other in our `Telegram group `_. Join us! diff --git a/telegram/__init__.py b/telegram/__init__.py index 5e0f3eaac3b..1230716e785 100644 --- a/telegram/__init__.py +++ b/telegram/__init__.py @@ -242,6 +242,7 @@ "warnings", ) +from pathlib import Path from . import _version, constants, error, helpers, request, warnings from ._birthdate import Birthdate @@ -442,6 +443,7 @@ from ._update import Update from ._user import User from ._userprofilephotos import UserProfilePhotos +from ._utils.warnings import warn from ._videochat import ( VideoChatEnded, VideoChatParticipantsInvited, @@ -475,3 +477,28 @@ #: #: .. versionadded:: 20.0 __bot_api_version_info__: constants._BotAPIVersion = _version.__bot_api_version_info__ + + +if not (Path(__file__).parent.resolve().absolute() / "ext").exists(): + _MESSAGE = ( + "Hey. You seem to be using the `python-telegram-bot-raw` library. " + "Please note that this libray has been deprecated and will no longer be updated. " + "Please instead use the `python-telegram-bot` library. The change requires no " + "changes in your code and requires no additional dependencies. For additional " + "information, please see the channel post at " + "https://t.me/pythontelegrambotchannel/145." + ) + + # DeprecationWarning is ignored by default in Python 3.7 and later by default outside + # __main__ modules. We use both warning categories to increase the chance of the user + # seeing the warning. + + warn( + warnings.PTBDeprecationWarning(version="NEXT.VERSION", message=_MESSAGE), + stacklevel=2, + ) + warn( + message=_MESSAGE, + category=warnings.PTBUserWarning, + stacklevel=2, + ) diff --git a/telegram/_utils/datetime.py b/telegram/_utils/datetime.py index 9790e27857d..1c0da085434 100644 --- a/telegram/_utils/datetime.py +++ b/telegram/_utils/datetime.py @@ -194,7 +194,7 @@ def extract_tzinfo_from_defaults(bot: "Bot") -> Union[dtm.tzinfo, None]: If the bot has no default values, :obj:`None` is returned. """ # We don't use `ininstance(bot, ExtBot)` here so that this works - # in `python-telegram-bot-raw` as well + # without the job-queue extra dependencies as well if hasattr(bot, "defaults") and bot.defaults: return bot.defaults.tzinfo return None From 078d7752502f543b2b3b34090298eb5ab125a734 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 19:25:54 +0200 Subject: [PATCH 5/9] Bump `pytest` from 8.2.0 to 8.2.1 (#4272) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 9704921e59d..63f6432ad4a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,7 +2,7 @@ pre-commit # needed for pre-commit hooks in the git commit command # For the test suite setuptools # required for test_meta -pytest==8.2.0 +pytest==8.2.1 pytest-asyncio==0.21.2 # needed because pytest doesn't come with native support for coroutines as tests pytest-xdist==3.6.1 # xdist runs tests in parallel flaky # Used for flaky tests (flaky decorator) From cf728496e4ffb9b9f9aaa4c3518d9f8898d2b4d9 Mon Sep 17 00:00:00 2001 From: Harshil <37377066+harshil21@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:39:31 -0400 Subject: [PATCH 6/9] API 7.4 (#4276, #4278, #4279, #4280, #4286, #4283, #4285) Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com> --- README.rst | 4 +- README_RAW.rst | 4 +- docs/source/inclusions/bot_methods.rst | 2 + docs/substitutions/global.rst | 6 + telegram/_bot.py | 226 ++++++++++++++++-- telegram/_callbackquery.py | 5 + telegram/_chat.py | 46 +++- telegram/_files/inputmedia.py | 39 ++- telegram/_inline/inlinekeyboardbutton.py | 35 ++- .../_inline/inlinequeryresultcachedgif.py | 9 + .../inlinequeryresultcachedmpeg4gif.py | 9 + .../_inline/inlinequeryresultcachedphoto.py | 9 + .../_inline/inlinequeryresultcachedvideo.py | 9 + telegram/_inline/inlinequeryresultgif.py | 9 + telegram/_inline/inlinequeryresultmpeg4gif.py | 7 + telegram/_inline/inlinequeryresultphoto.py | 9 + telegram/_inline/inlinequeryresultvideo.py | 9 + .../_inline/inputinvoicemessagecontent.py | 67 +++--- telegram/_keyboardbutton.py | 3 +- telegram/_message.py | 89 ++++++- telegram/_messageentity.py | 71 +++--- telegram/_payment/invoice.py | 6 +- telegram/_payment/precheckoutquery.py | 6 +- telegram/_payment/successfulpayment.py | 6 +- telegram/_user.py | 78 +++++- telegram/constants.py | 67 +++--- telegram/ext/_extbot.py | 73 +++++- telegram/ext/filters.py | 14 ++ tests/_files/test_animation.py | 2 + tests/_files/test_inputmedia.py | 21 ++ tests/_files/test_photo.py | 2 + tests/_files/test_video.py | 2 + .../test_inlinequeryresultcachedgif.py | 10 + .../test_inlinequeryresultcachedmpeg4gif.py | 10 + .../test_inlinequeryresultcachedphoto.py | 10 + .../test_inlinequeryresultcachedvideo.py | 10 + tests/_inline/test_inlinequeryresultgif.py | 7 + .../_inline/test_inlinequeryresultmpeg4gif.py | 9 + tests/_inline/test_inlinequeryresultphoto.py | 7 + tests/_inline/test_inlinequeryresultvideo.py | 7 + tests/_payment/test_invoice.py | 7 +- tests/ext/test_filters.py | 5 + tests/test_bot.py | 24 ++ tests/test_chat.py | 4 +- tests/test_constants.py | 3 +- tests/test_message.py | 37 ++- tests/test_official/exceptions.py | 4 +- tests/test_user.py | 19 +- 48 files changed, 952 insertions(+), 165 deletions(-) diff --git a/README.rst b/README.rst index d9a61b5c95c..c3b29aa626f 100644 --- a/README.rst +++ b/README.rst @@ -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.3-blue?logo=telegram +.. image:: https://img.shields.io/badge/Bot%20API-7.4-blue?logo=telegram :target: https://core.telegram.org/bots/api-changelog :alt: Supported Bot API version @@ -79,7 +79,7 @@ make the development of bots easy and straightforward. These classes are contain Telegram API support ==================== -All types and methods of the Telegram Bot API **7.3** are supported. +All types and methods of the Telegram Bot API **7.4** are supported. Installing ========== diff --git a/README_RAW.rst b/README_RAW.rst index 64f83121e21..7278f47aeeb 100644 --- a/README_RAW.rst +++ b/README_RAW.rst @@ -11,7 +11,7 @@ :target: https://pypi.org/project/python-telegram-bot-raw/ :alt: Supported Python versions -.. image:: https://img.shields.io/badge/Bot%20API-7.3-blue?logo=telegram +.. image:: https://img.shields.io/badge/Bot%20API-7.4-blue?logo=telegram :target: https://core.telegram.org/bots/api-changelog :alt: Supported Bot API version @@ -92,7 +92,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju Telegram API support ==================== -All types and methods of the Telegram Bot API **7.3** are supported. +All types and methods of the Telegram Bot API **7.4** are supported. Installing ========== diff --git a/docs/source/inclusions/bot_methods.rst b/docs/source/inclusions/bot_methods.rst index 9dcfa1982e2..bece5296e22 100644 --- a/docs/source/inclusions/bot_methods.rst +++ b/docs/source/inclusions/bot_methods.rst @@ -369,6 +369,8 @@ - Used for getting basic info about a file * - :meth:`~telegram.Bot.get_me` - Used for getting basic information about the bot + * - :meth:`~telegram.Bot.refund_star_payment` + - Used for refunding a payment in Telegram Stars .. raw:: html diff --git a/docs/substitutions/global.rst b/docs/substitutions/global.rst index 36038e71eba..37edc74a446 100644 --- a/docs/substitutions/global.rst +++ b/docs/substitutions/global.rst @@ -81,3 +81,9 @@ .. |non_optional_story_argument| replace:: As of this version, this argument is now required. In accordance with our `stability policy `__, the signature will be kept as optional for now, though they are mandatory and an error will be raised if you don't pass it. .. |business_id_str| replace:: Unique identifier of the business connection on behalf of which the message will be sent. + +.. |message_effect_id| replace:: Unique identifier of the message effect to be added to the message; for private chats only. + +.. |show_cap_above_med| replace:: :obj:`True`, if the caption must be shown above the message media. + +.. |tg_stars| replace:: `Telegram Stars `__ diff --git a/telegram/_bot.py b/telegram/_bot.py index 3ae0ce5f17d..94e70fe6c3d 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -672,6 +672,7 @@ async def _send_message( link_preview_options: ODVInput["LinkPreviewOptions"] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -711,7 +712,9 @@ async def _send_message( data["disable_notification"] = disable_notification data["protect_content"] = protect_content data["parse_mode"] = parse_mode - data["reply_parameters"] = reply_parameters + + if reply_parameters is not None: + data["reply_parameters"] = reply_parameters if link_preview_options is not None: data["link_preview_options"] = link_preview_options @@ -731,6 +734,9 @@ async def _send_message( if business_connection_id is not None: data["business_connection_id"] = business_connection_id + if message_effect_id is not None: + data["message_effect_id"] = message_effect_id + result = await self._post( endpoint, data, @@ -919,6 +925,7 @@ async def send_message( link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -967,6 +974,9 @@ async def send_message( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -1024,6 +1034,7 @@ async def send_message( parse_mode=parse_mode, link_preview_options=link_preview_options, reply_parameters=reply_parameters, + message_effect_id=message_effect_id, read_timeout=read_timeout, write_timeout=write_timeout, connect_timeout=connect_timeout, @@ -1272,6 +1283,8 @@ async def send_photo( has_spoiler: Optional[bool] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -1335,6 +1348,12 @@ async def send_photo( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -1372,6 +1391,7 @@ async def send_photo( "chat_id": chat_id, "photo": self._parse_file_input(photo, PhotoSize, filename=filename), "has_spoiler": has_spoiler, + "show_caption_above_media": show_caption_above_media, } return await self._send_message( @@ -1393,6 +1413,7 @@ async def send_photo( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_audio( @@ -1412,6 +1433,7 @@ async def send_audio( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -1484,6 +1506,9 @@ async def send_audio( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -1545,6 +1570,7 @@ async def send_audio( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_document( @@ -1562,6 +1588,7 @@ async def send_document( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -1633,6 +1660,9 @@ async def send_document( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -1690,6 +1720,7 @@ async def send_document( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_sticker( @@ -1703,6 +1734,7 @@ async def send_sticker( emoji: Optional[str] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -1754,6 +1786,9 @@ async def send_sticker( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -1803,6 +1838,7 @@ async def send_sticker( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_video( @@ -1824,6 +1860,8 @@ async def send_video( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -1904,6 +1942,12 @@ async def send_video( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -1946,6 +1990,7 @@ async def send_video( "supports_streaming": supports_streaming, "thumbnail": self._parse_file_input(thumbnail, attach=True) if thumbnail else None, "has_spoiler": has_spoiler, + "show_caption_above_media": show_caption_above_media, } return await self._send_message( @@ -1967,6 +2012,7 @@ async def send_video( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_video_note( @@ -1982,6 +2028,7 @@ async def send_video_note( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -2047,6 +2094,9 @@ async def send_video_note( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -2104,6 +2154,7 @@ async def send_video_note( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_animation( @@ -2124,6 +2175,8 @@ async def send_animation( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -2198,6 +2251,12 @@ async def send_animation( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -2239,6 +2298,7 @@ async def send_animation( "height": height, "thumbnail": self._parse_file_input(thumbnail, attach=True) if thumbnail else None, "has_spoiler": has_spoiler, + "show_caption_above_media": show_caption_above_media, } return await self._send_message( @@ -2260,6 +2320,7 @@ async def send_animation( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_voice( @@ -2276,6 +2337,7 @@ async def send_voice( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -2344,6 +2406,9 @@ async def send_voice( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -2402,6 +2467,7 @@ async def send_voice( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_media_group( @@ -2415,6 +2481,7 @@ async def send_media_group( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -2465,6 +2532,9 @@ async def send_media_group( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -2558,6 +2628,7 @@ async def send_media_group( "message_thread_id": message_thread_id, "reply_parameters": reply_parameters, "business_connection_id": business_connection_id, + "message_effect_id": message_effect_id, } result = await self._post( @@ -2587,6 +2658,7 @@ async def send_location( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -2643,6 +2715,9 @@ async def send_location( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -2712,6 +2787,7 @@ async def send_location( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def edit_message_live_location( @@ -2883,6 +2959,7 @@ async def send_venue( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -2935,6 +3012,9 @@ async def send_venue( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -3015,6 +3095,7 @@ async def send_venue( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_contact( @@ -3030,6 +3111,7 @@ async def send_contact( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -3072,6 +3154,9 @@ async def send_contact( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -3143,6 +3228,7 @@ async def send_contact( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_game( @@ -3155,6 +3241,7 @@ async def send_game( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -3187,6 +3274,9 @@ async def send_game( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -3233,6 +3323,7 @@ async def send_game( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_chat_action( @@ -3954,6 +4045,7 @@ async def edit_message_caption( reply_markup: Optional["InlineKeyboardMarkup"] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence["MessageEntity"]] = None, + show_caption_above_media: Optional[bool] = None, *, read_timeout: ODVInput[float] = DEFAULT_NONE, write_timeout: ODVInput[float] = DEFAULT_NONE, @@ -3985,6 +4077,9 @@ async def edit_message_caption( |sequenceargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): An object for an inline keyboard. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Returns: :class:`telegram.Message`: On success, if edited message is not an inline message, the @@ -3998,6 +4093,7 @@ async def edit_message_caption( "chat_id": chat_id, "message_id": message_id, "inline_message_id": inline_message_id, + "show_caption_above_media": show_caption_above_media, } return await self._send_message( @@ -4822,7 +4918,7 @@ async def send_invoice( title: str, description: str, payload: str, - provider_token: str, + provider_token: Optional[str], # This arg is now optional as of Bot API 7.4 currency: str, prices: Sequence["LabeledPrice"], start_parameter: Optional[str] = None, @@ -4845,6 +4941,7 @@ async def send_invoice( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -4876,12 +4973,19 @@ async def send_invoice( :tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be displayed to the user, use for your internal processes. provider_token (:obj:`str`): Payments provider token, obtained via - `@BotFather `_. + `@BotFather `_. Pass an empty string for payments in + |tg_stars|. + + .. deprecated:: NEXT.VERSION + As of Bot API 7.4, this parameter is now optional and future versions of the + library will make it optional as well. + currency (:obj:`str`): Three-letter ISO 4217 currency code, see `more on currencies - `_. + `_. Pass ``XTR`` for + payment in |tg_stars|. prices (Sequence[:class:`telegram.LabeledPrice`]): Price breakdown, a sequence of components (e.g. product price, tax, discount, delivery cost, delivery tax, - bonus, etc.). + bonus, etc.). Must contain exactly one item for payment in |tg_stars|. .. versionchanged:: 20.0 |sequenceargs| @@ -4890,7 +4994,7 @@ async def send_invoice( a maximum tip of US$ 1.45 pass ``max_tip_amount = 145``. See the exp parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the - majority of currencies). Defaults to ``0``. + majority of currencies). Defaults to ``0``. Not supported for payment in |tg_stars| .. versionadded:: 13.5 suggested_tip_amounts (Sequence[:obj:`int`], optional): An array of @@ -4923,19 +5027,20 @@ async def send_invoice( photo_width (:obj:`int`, optional): Photo width. photo_height (:obj:`int`, optional): Photo height. need_name (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's full - name to complete the order. + name to complete the order. Ignored for payments in |tg_stars|. need_phone_number (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's - phone number to complete the order. + phone number to complete the order. Ignored for payments in |tg_stars|. need_email (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's email - to complete the order. + to complete the order. Ignored for payments in |tg_stars|. need_shipping_address (:obj:`bool`, optional): Pass :obj:`True`, if you require the - user's shipping address to complete the order. + user's shipping address to complete the order. Ignored for payments in + |tg_stars|. send_phone_number_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's - phone number should be sent to provider. + phone number should be sent to provider. Ignored for payments in |tg_stars|. send_email_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's email - address should be sent to provider. + address should be sent to provider. Ignored for payments in |tg_stars|. is_flexible (:obj:`bool`, optional): Pass :obj:`True`, if the final price depends on - the shipping method. + the shipping method. Ignored for payments in |tg_stars|. disable_notification (:obj:`bool`, optional): |disable_notification| protect_content (:obj:`bool`, optional): |protect_content| @@ -4950,6 +5055,9 @@ async def send_invoice( reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| .. versionadded:: 20.8 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -5018,6 +5126,7 @@ async def send_invoice( connect_timeout=connect_timeout, pool_timeout=pool_timeout, api_kwargs=api_kwargs, + message_effect_id=message_effect_id, ) async def answer_shipping_query( @@ -6839,6 +6948,7 @@ async def send_poll( business_connection_id: Optional[str] = None, question_parse_mode: ODVInput[str] = DEFAULT_NONE, question_entities: Optional[Sequence["MessageEntity"]] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -6933,6 +7043,9 @@ async def send_poll( :paramref:`question_parse_mode`. .. versionadded:: 21.2 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -6998,6 +7111,7 @@ async def send_poll( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def stop_poll( @@ -7055,6 +7169,7 @@ async def send_dice( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -7101,6 +7216,9 @@ async def send_dice( business_connection_id (:obj:`str`, optional): |business_id_str| .. versionadded:: 21.1 + message_effect_id (:obj:`str`, optional): |message_effect_id| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -7148,6 +7266,7 @@ async def send_dice( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def get_my_default_administrator_rights( @@ -7476,6 +7595,7 @@ async def copy_message( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -7519,6 +7639,9 @@ async def copy_message( reply_parameters (:class:`telegram.ReplyParameters`, optional): |reply_parameters| .. versionadded:: 20.8 + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -7575,6 +7698,7 @@ async def copy_message( "reply_markup": reply_markup, "message_thread_id": message_thread_id, "reply_parameters": reply_parameters, + "show_caption_above_media": show_caption_above_media, } result = await self._post( @@ -7743,7 +7867,7 @@ async def create_invoice_link( title: str, description: str, payload: str, - provider_token: str, + provider_token: Optional[str], # This arg is now optional as of Bot API 7.4 currency: str, prices: Sequence["LabeledPrice"], max_tip_amount: Optional[int] = None, @@ -7782,12 +7906,19 @@ async def create_invoice_link( :tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be displayed to the user, use for your internal processes. provider_token (:obj:`str`): Payments provider token, obtained via - `@BotFather `_. + `@BotFather `_. Pass an empty string for payments in + |tg_stars|. + + .. deprecated:: NEXT.VERSION + As of Bot API 7.4, this parameter is now optional and future versions of the + library will make it optional as well. + currency (:obj:`str`): Three-letter ISO 4217 currency code, see `more on currencies - `_. + `_. Pass ``XTR`` for + payments in |tg_stars|. prices (Sequence[:class:`telegram.LabeledPrice`)]: Price breakdown, a sequence of components (e.g. product price, tax, discount, delivery cost, delivery tax, - bonus, etc.). + bonus, etc.). Must contain exactly one item for payments in |tg_stars|. .. versionchanged:: 20.0 |sequenceargs| @@ -7796,7 +7927,8 @@ async def create_invoice_link( a maximum tip of US$ 1.45 pass ``max_tip_amount = 145``. See the exp parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the - majority of currencies). Defaults to ``0``. + majority of currencies). Defaults to ``0``. Not supported for payments in + |tg_stars|. suggested_tip_amounts (Sequence[:obj:`int`], optional): An array of suggested amounts of tips in the *smallest* units of the currency (integer, **not** float/double). At most :tg-const:`telegram.Invoice.MAX_TIP_AMOUNTS` suggested tip @@ -7815,19 +7947,20 @@ async def create_invoice_link( photo_width (:obj:`int`, optional): Photo width. photo_height (:obj:`int`, optional): Photo height. need_name (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's full - name to complete the order. + name to complete the order. Ignored for payments in |tg_stars|. need_phone_number (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's - phone number to complete the order. + phone number to complete the order. Ignored for payments in |tg_stars|. need_email (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's email - address to complete the order. + address to complete the order. Ignored for payments in |tg_stars|. need_shipping_address (:obj:`bool`, optional): Pass :obj:`True`, if you require the - user's shipping address to complete the order. + user's shipping address to complete the order. Ignored for payments in + |tg_stars|. send_phone_number_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's - phone number should be sent to provider. + phone number should be sent to provider. Ignored for payments in |tg_stars|. send_email_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's email - address should be sent to provider. + address should be sent to provider. Ignored for payments in |tg_stars|. is_flexible (:obj:`bool`, optional): Pass :obj:`True`, if the final price depends on - the shipping method. + the shipping method. Ignored for payments in |tg_stars|. Returns: :class:`str`: On success, the created invoice link is returned. @@ -8895,6 +9028,47 @@ async def replace_sticker_in_set( api_kwargs=api_kwargs, ) + async def refund_star_payment( + self, + user_id: int, + telegram_payment_charge_id: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Refunds a successful payment in |tg_stars|. + + .. versionadded:: NEXT.VERSION + + Args: + user_id (:obj:`int`): User identifier of the user whose payment will be refunded. + telegram_payment_charge_id (:obj:`str`): Telegram payment identifier. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "user_id": user_id, + "telegram_payment_charge_id": telegram_payment_charge_id, + } + + return await self._post( + "refundStarPayment", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + 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} @@ -9145,3 +9319,5 @@ def to_dict(self, recursive: bool = True) -> JSONDict: # noqa: ARG002 """Alias for :meth:`get_business_connection`""" replaceStickerInSet = replace_sticker_in_set """Alias for :meth:`replace_sticker_in_set`""" + refundStarPayment = refund_star_payment + """Alias for :meth:`refund_star_payment`""" diff --git a/telegram/_callbackquery.py b/telegram/_callbackquery.py index 3df7089c997..af89c784b10 100644 --- a/telegram/_callbackquery.py +++ b/telegram/_callbackquery.py @@ -281,6 +281,7 @@ async def edit_message_caption( reply_markup: Optional["InlineKeyboardMarkup"] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence["MessageEntity"]] = None, + show_caption_above_media: Optional[bool] = None, *, read_timeout: ODVInput[float] = DEFAULT_NONE, write_timeout: ODVInput[float] = DEFAULT_NONE, @@ -326,6 +327,7 @@ async def edit_message_caption( caption_entities=caption_entities, chat_id=None, message_id=None, + show_caption_above_media=show_caption_above_media, ) return await self._get_message().edit_caption( caption=caption, @@ -337,6 +339,7 @@ async def edit_message_caption( parse_mode=parse_mode, api_kwargs=api_kwargs, caption_entities=caption_entities, + show_caption_above_media=show_caption_above_media, ) async def edit_message_reply_markup( @@ -815,6 +818,7 @@ async def copy_message( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, *, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: Optional[int] = None, @@ -861,6 +865,7 @@ async def copy_message( protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, + show_caption_above_media=show_caption_above_media, ) MAX_ANSWER_TEXT_LENGTH: Final[int] = ( diff --git a/telegram/_chat.py b/telegram/_chat.py index 8250a8f179b..39acf55aacc 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -1005,6 +1005,7 @@ async def send_message( link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1045,6 +1046,7 @@ async def send_message( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def delete_message( @@ -1121,6 +1123,7 @@ async def send_media_group( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1162,6 +1165,7 @@ async def send_media_group( caption_entities=caption_entities, reply_parameters=reply_parameters, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_chat_action( @@ -1214,6 +1218,8 @@ async def send_photo( has_spoiler: Optional[bool] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1255,6 +1261,8 @@ async def send_photo( api_kwargs=api_kwargs, has_spoiler=has_spoiler, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, ) async def send_contact( @@ -1269,6 +1277,7 @@ async def send_contact( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1309,6 +1318,7 @@ async def send_contact( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_audio( @@ -1327,6 +1337,7 @@ async def send_audio( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1371,6 +1382,7 @@ async def send_audio( api_kwargs=api_kwargs, thumbnail=thumbnail, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_document( @@ -1387,6 +1399,7 @@ async def send_document( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1429,6 +1442,7 @@ async def send_document( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_dice( @@ -1440,6 +1454,7 @@ async def send_dice( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1475,6 +1490,7 @@ async def send_dice( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_game( @@ -1486,6 +1502,7 @@ async def send_game( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1521,6 +1538,7 @@ async def send_game( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_invoice( @@ -1528,7 +1546,7 @@ async def send_invoice( title: str, description: str, payload: str, - provider_token: str, + provider_token: Optional[str], currency: str, prices: Sequence["LabeledPrice"], start_parameter: Optional[str] = None, @@ -1551,6 +1569,7 @@ async def send_invoice( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1615,6 +1634,7 @@ async def send_invoice( protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, + message_effect_id=message_effect_id, ) async def send_location( @@ -1631,6 +1651,7 @@ async def send_location( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1673,6 +1694,7 @@ async def send_location( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_animation( @@ -1692,6 +1714,8 @@ async def send_animation( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1737,6 +1761,8 @@ async def send_animation( has_spoiler=has_spoiler, thumbnail=thumbnail, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, ) async def send_sticker( @@ -1749,6 +1775,7 @@ async def send_sticker( emoji: Optional[str] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1785,6 +1812,7 @@ async def send_sticker( message_thread_id=message_thread_id, emoji=emoji, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_venue( @@ -1803,6 +1831,7 @@ async def send_venue( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1847,6 +1876,7 @@ async def send_venue( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_video( @@ -1867,6 +1897,8 @@ async def send_video( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1913,6 +1945,8 @@ async def send_video( message_thread_id=message_thread_id, has_spoiler=has_spoiler, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, ) async def send_video_note( @@ -1927,6 +1961,7 @@ async def send_video_note( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1967,6 +2002,7 @@ async def send_video_note( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_voice( @@ -1982,6 +2018,7 @@ async def send_voice( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2023,6 +2060,7 @@ async def send_voice( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_poll( @@ -2047,6 +2085,7 @@ async def send_poll( business_connection_id: Optional[str] = None, question_parse_mode: ODVInput[str] = DEFAULT_NONE, question_entities: Optional[Sequence["MessageEntity"]] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2083,6 +2122,7 @@ async def send_poll( write_timeout=write_timeout, connect_timeout=connect_timeout, pool_timeout=pool_timeout, + message_effect_id=message_effect_id, explanation=explanation, explanation_parse_mode=explanation_parse_mode, open_period=open_period, @@ -2109,6 +2149,7 @@ async def send_copy( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2149,6 +2190,7 @@ async def send_copy( api_kwargs=api_kwargs, protect_content=protect_content, message_thread_id=message_thread_id, + show_caption_above_media=show_caption_above_media, ) async def copy_message( @@ -2163,6 +2205,7 @@ async def copy_message( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2203,6 +2246,7 @@ async def copy_message( api_kwargs=api_kwargs, protect_content=protect_content, message_thread_id=message_thread_id, + show_caption_above_media=show_caption_above_media, ) async def send_copies( diff --git a/telegram/_files/inputmedia.py b/telegram/_files/inputmedia.py index 163b3a62a2f..229e7320907 100644 --- a/telegram/_files/inputmedia.py +++ b/telegram/_files/inputmedia.py @@ -160,6 +160,9 @@ class InputMediaAnimation(InputMedia): optional): |thumbdocstringnopath| .. versionadded:: 20.2 + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.ANIMATION`. @@ -184,9 +187,19 @@ class InputMediaAnimation(InputMedia): thumbnail (:class:`telegram.InputFile`): Optional. |thumbdocstringbase| .. versionadded:: 20.2 + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: NEXT.VERSION """ - __slots__ = ("duration", "has_spoiler", "height", "thumbnail", "width") + __slots__ = ( + "duration", + "has_spoiler", + "height", + "show_caption_above_media", + "thumbnail", + "width", + ) def __init__( self, @@ -200,6 +213,7 @@ def __init__( filename: Optional[str] = None, has_spoiler: Optional[bool] = None, thumbnail: Optional[FileInput] = None, + show_caption_above_media: Optional[bool] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -229,6 +243,7 @@ def __init__( self.height: Optional[int] = height self.duration: Optional[int] = duration self.has_spoiler: Optional[bool] = has_spoiler + self.show_caption_above_media: Optional[bool] = show_caption_above_media class InputMediaPhoto(InputMedia): @@ -260,6 +275,9 @@ class InputMediaPhoto(InputMedia): with a spoiler animation. .. versionadded:: 20.0 + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.PHOTO`. @@ -278,9 +296,15 @@ class InputMediaPhoto(InputMedia): spoiler animation. .. versionadded:: 20.0 + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: NEXT.VERSION """ - __slots__ = ("has_spoiler",) + __slots__ = ( + "has_spoiler", + "show_caption_above_media", + ) def __init__( self, @@ -290,6 +314,7 @@ def __init__( caption_entities: Optional[Sequence[MessageEntity]] = None, filename: Optional[str] = None, has_spoiler: Optional[bool] = None, + show_caption_above_media: Optional[bool] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -307,6 +332,7 @@ def __init__( with self._unfrozen(): self.has_spoiler: Optional[bool] = has_spoiler + self.show_caption_above_media: Optional[bool] = show_caption_above_media class InputMediaVideo(InputMedia): @@ -359,6 +385,9 @@ class InputMediaVideo(InputMedia): optional): |thumbdocstringnopath| .. versionadded:: 20.2 + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.VIDEO`. @@ -385,12 +414,16 @@ class InputMediaVideo(InputMedia): thumbnail (:class:`telegram.InputFile`): Optional. |thumbdocstringbase| .. versionadded:: 20.2 + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: NEXT.VERSION """ __slots__ = ( "duration", "has_spoiler", "height", + "show_caption_above_media", "supports_streaming", "thumbnail", "width", @@ -409,6 +442,7 @@ def __init__( filename: Optional[str] = None, has_spoiler: Optional[bool] = None, thumbnail: Optional[FileInput] = None, + show_caption_above_media: Optional[bool] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -439,6 +473,7 @@ def __init__( ) self.supports_streaming: Optional[bool] = supports_streaming self.has_spoiler: Optional[bool] = has_spoiler + self.show_caption_above_media: Optional[bool] = show_caption_above_media class InputMediaAudio(InputMedia): diff --git a/telegram/_inline/inlinekeyboardbutton.py b/telegram/_inline/inlinekeyboardbutton.py index 9af5d14eda6..d88f222cad7 100644 --- a/telegram/_inline/inlinekeyboardbutton.py +++ b/telegram/_inline/inlinekeyboardbutton.py @@ -41,7 +41,8 @@ class InlineKeyboardButton(TelegramObject): :attr:`web_app` and :attr:`pay` are equal. Note: - * You must use exactly one of the optional fields. Mind that :attr:`callback_game` is not + * Exactly one of the optional fields must be used to specify type of the button. + * Mind that :attr:`callback_game` is not working as expected. Putting a game short name in it might, but is not guaranteed to work. * If your bot allows for arbitrary callback data, in keyboards returned in a response @@ -123,11 +124,17 @@ class InlineKeyboardButton(TelegramObject): insert the bot's username and the specified inline query in the input field. Not supported for messages sent on behalf of a Telegram Business account. callback_game (:class:`telegram.CallbackGame`, optional): Description of the game that will - be launched when the user presses the button. This type of button **must** always be - the **first** button in the first row. - pay (:obj:`bool`, optional): Specify :obj:`True`, to send a Pay button. This type of button - **must** always be the **first** button in the first row and can only be used in - invoice messages. + be launched when the user presses the button + + Note: + This type of button **must** always be the first button in the first row. + pay (:obj:`bool`, optional): Specify :obj:`True`, to send a Pay button. + Substrings ``“⭐️”`` and ``“XTR”`` in the buttons's text will be replaced with a + Telegram Star icon. + + Note: + This type of button **must** always be the first button in the first row and can + only be used in invoice messages. switch_inline_query_chosen_chat (:obj:`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 @@ -186,11 +193,17 @@ class InlineKeyboardButton(TelegramObject): insert the bot's username and the specified inline query in the input field. Not supported for messages sent on behalf of a Telegram Business account. callback_game (:class:`telegram.CallbackGame`): Optional. Description of the game that will - be launched when the user presses the button. This type of button **must** always be - the **first** button in the first row. - pay (:obj:`bool`): Optional. Specify :obj:`True`, to send a Pay button. This type of button - **must** always be the **first** button in the first row and can only be used in - invoice messages. + be launched when the user presses the button. + + Note: + This type of button **must** always be the first button in the first row. + pay (:obj:`bool`): Optional. Specify :obj:`True`, to send a Pay button. + Substrings ``“⭐️”`` and ``“XTR”`` in the buttons's text will be replaced with a + Telegram Star icon. + + Note: + This type of button **must** always be the first button in the first row and can + only be used in invoice messages. switch_inline_query_chosen_chat (:obj:`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 diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index ca7188014ca..9516accae04 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -59,6 +59,9 @@ class InlineQueryResultCachedGif(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the gif. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.GIF`. @@ -81,6 +84,9 @@ class InlineQueryResultCachedGif(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the message to be sent instead of the gif. + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: NEXT.VERSION """ @@ -91,6 +97,7 @@ class InlineQueryResultCachedGif(InlineQueryResult): "input_message_content", "parse_mode", "reply_markup", + "show_caption_above_media", "title", ) @@ -104,6 +111,7 @@ def __init__( input_message_content: Optional["InputMessageContent"] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence[MessageEntity]] = None, + show_caption_above_media: Optional[bool] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -119,3 +127,4 @@ def __init__( self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index f689734b80e..3fa2a8f1393 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -59,6 +59,9 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the MPEG-4 file. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.MPEG4GIF`. @@ -81,6 +84,9 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the message to be sent instead of the MPEG-4 file. + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: NEXT.VERSION """ @@ -91,6 +97,7 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): "mpeg4_file_id", "parse_mode", "reply_markup", + "show_caption_above_media", "title", ) @@ -104,6 +111,7 @@ def __init__( input_message_content: Optional["InputMessageContent"] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence[MessageEntity]] = None, + show_caption_above_media: Optional[bool] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -119,3 +127,4 @@ def __init__( self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index be484e280dd..6c18630fff2 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -60,6 +60,9 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the photo. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.PHOTO`. @@ -83,6 +86,9 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the message to be sent instead of the photo. + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: NEXT.VERSION """ @@ -94,6 +100,7 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): "parse_mode", "photo_file_id", "reply_markup", + "show_caption_above_media", "title", ) @@ -108,6 +115,7 @@ def __init__( input_message_content: Optional["InputMessageContent"] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence[MessageEntity]] = None, + show_caption_above_media: Optional[bool] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -124,3 +132,4 @@ def __init__( self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index e226d0e4f75..7ce8c423faa 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -56,6 +56,9 @@ class InlineQueryResultCachedVideo(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the video. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VIDEO`. @@ -79,6 +82,9 @@ class InlineQueryResultCachedVideo(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the message to be sent instead of the video. + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: NEXT.VERSION """ @@ -89,6 +95,7 @@ class InlineQueryResultCachedVideo(InlineQueryResult): "input_message_content", "parse_mode", "reply_markup", + "show_caption_above_media", "title", "video_file_id", ) @@ -104,6 +111,7 @@ def __init__( input_message_content: Optional["InputMessageContent"] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence[MessageEntity]] = None, + show_caption_above_media: Optional[bool] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -120,3 +128,4 @@ def __init__( self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index 1a889a1bb04..ab68f083daa 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -78,6 +78,9 @@ class InlineQueryResultGif(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the GIF animation. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Raises: :class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is @@ -115,6 +118,9 @@ class InlineQueryResultGif(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the message to be sent instead of the GIF animation. + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: NEXT.VERSION """ @@ -128,6 +134,7 @@ class InlineQueryResultGif(InlineQueryResult): "input_message_content", "parse_mode", "reply_markup", + "show_caption_above_media", "thumbnail_mime_type", "thumbnail_url", "title", @@ -148,6 +155,7 @@ def __init__( parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence[MessageEntity]] = None, thumbnail_mime_type: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -168,3 +176,4 @@ def __init__( self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content self.thumbnail_mime_type: Optional[str] = thumbnail_mime_type + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index a744a040010..6478340f745 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -80,7 +80,9 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the video animation. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + .. versionadded:: NEXT.VERSION Raises: :class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is supplied or if both are supplied and are not equal. @@ -118,7 +120,9 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the message to be sent instead of the video animation. + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + .. versionadded:: NEXT.VERSION """ __slots__ = ( @@ -131,6 +135,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): "mpeg4_width", "parse_mode", "reply_markup", + "show_caption_above_media", "thumbnail_mime_type", "thumbnail_url", "title", @@ -151,6 +156,7 @@ def __init__( parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence[MessageEntity]] = None, thumbnail_mime_type: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -171,3 +177,4 @@ def __init__( self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content self.thumbnail_mime_type: Optional[str] = thumbnail_mime_type + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index 5eef1acf66f..1fbb4f4d649 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -74,6 +74,9 @@ class InlineQueryResultPhoto(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the photo. + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Raises: :class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is @@ -105,6 +108,9 @@ class InlineQueryResultPhoto(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the message to be sent instead of the photo. + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: NEXT.VERSION """ @@ -118,6 +124,7 @@ class InlineQueryResultPhoto(InlineQueryResult): "photo_url", "photo_width", "reply_markup", + "show_caption_above_media", "thumbnail_url", "title", ) @@ -136,6 +143,7 @@ def __init__( input_message_content: Optional["InputMessageContent"] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence[MessageEntity]] = None, + show_caption_above_media: Optional[bool] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -155,3 +163,4 @@ def __init__( self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index 90134c450b0..2c387b17c46 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -88,6 +88,9 @@ class InlineQueryResultVideo(InlineQueryResult): message to be sent instead of the video. This field is required if ``InlineQueryResultVideo`` is used to send an HTML-page as a result (e.g., a YouTube video). + show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| + + .. versionadded:: NEXT.VERSION Raises: :class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is @@ -127,6 +130,9 @@ class InlineQueryResultVideo(InlineQueryResult): message to be sent instead of the video. This field is required if ``InlineQueryResultVideo`` is used to send an HTML-page as a result (e.g., a YouTube video). + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: NEXT.VERSION """ @@ -138,6 +144,7 @@ class InlineQueryResultVideo(InlineQueryResult): "mime_type", "parse_mode", "reply_markup", + "show_caption_above_media", "thumbnail_url", "title", "video_duration", @@ -162,6 +169,7 @@ def __init__( input_message_content: Optional["InputMessageContent"] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence[MessageEntity]] = None, + show_caption_above_media: Optional[bool] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -183,3 +191,4 @@ def __init__( self.description: Optional[str] = description self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content + self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index 64b9ff93b55..d710085fdab 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -49,12 +49,18 @@ class InputInvoiceMessageContent(InputMessageContent): :tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be displayed to the user, use for your internal processes. provider_token (:obj:`str`): Payment provider token, obtained via - `@Botfather `_. + `@Botfather `_. Pass an empty string for payments in + |tg_stars|. + + .. deprecated:: NEXT.VERSION + As of Bot API 7.4, this parameter is now optional and future versions of the + library will make it optional as well. currency (:obj:`str`): Three-letter ISO 4217 currency code, see more on - `currencies `_ + `currencies `_. + Pass ``XTR`` for payments in |tg_stars|. prices (Sequence[:class:`telegram.LabeledPrice`]): Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, - etc.) + etc.). Must contain exactly one item for payments in |tg_stars|. .. versionchanged:: 20.0 |sequenceclassargs| @@ -64,7 +70,8 @@ class InputInvoiceMessageContent(InputMessageContent): maximum tip of US$ 1.45 pass ``max_tip_amount = 145``. See the ``exp`` parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority - of currencies). Defaults to ``0``. + of currencies). Defaults to ``0``. Defaults to ``0``. Not supported for payments in + |tg_stars|. suggested_tip_amounts (Sequence[:obj:`int`], optional): An array of suggested amounts of tip in the *smallest* units of the currency (integer, **not** float/double). At most 4 suggested tip amounts can be specified. The suggested tip amounts must be @@ -85,20 +92,20 @@ class InputInvoiceMessageContent(InputMessageContent): photo_size (:obj:`int`, optional): Photo size. photo_width (:obj:`int`, optional): Photo width. photo_height (:obj:`int`, optional): Photo height. - need_name (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's full name to - complete the order. + need_name (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's full + name to complete the order. Ignored for payments in |tg_stars|. need_phone_number (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's - phone number to complete the order + phone number to complete the order. Ignored for payments in |tg_stars|. need_email (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's email - address to complete the order. - need_shipping_address (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's - shipping address to complete the order - send_phone_number_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's phone - number should be sent to provider. - send_email_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's email address - should be sent to provider. - is_flexible (:obj:`bool`, optional): Pass :obj:`True`, if the final price depends on the - shipping method. + address to complete the order. Ignored for payments in |tg_stars|. + need_shipping_address (:obj:`bool`, optional): Pass :obj:`True`, if you require the + user's shipping address to complete the order. Ignored for payments in |tg_stars| + send_phone_number_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's + phone number should be sent to provider. Ignored for payments in |tg_stars|. + send_email_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's email + address should be sent to provider. Ignored for payments in |tg_stars|. + is_flexible (:obj:`bool`, optional): Pass :obj:`True`, if the final price depends on + the shipping method. Ignored for payments in |tg_stars|. Attributes: title (:obj:`str`): Product name. :tg-const:`telegram.Invoice.MIN_TITLE_LENGTH`- @@ -111,12 +118,14 @@ class InputInvoiceMessageContent(InputMessageContent): :tg-const:`telegram.Invoice.MAX_PAYLOAD_LENGTH` bytes. This will not be displayed to the user, use for your internal processes. provider_token (:obj:`str`): Payment provider token, obtained via - `@Botfather `_. + `@Botfather `_. Pass an empty string for payments in `Telegram + Stars `_. currency (:obj:`str`): Three-letter ISO 4217 currency code, see more on - `currencies `_ + `currencies `_. + Pass ``XTR`` for payments in |tg_stars|. prices (Tuple[:class:`telegram.LabeledPrice`]): Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, - etc.) + etc.). Must contain exactly one item for payments in |tg_stars|. .. versionchanged:: 20.0 |tupleclassattrs| @@ -126,7 +135,7 @@ class InputInvoiceMessageContent(InputMessageContent): maximum tip of US$ 1.45 ``max_tip_amount`` is ``145``. See the ``exp`` parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority - of currencies). Defaults to ``0``. + of currencies). Defaults to ``0``. Not supported for payments in |tg_stars|. suggested_tip_amounts (Tuple[:obj:`int`]): Optional. An array of suggested amounts of tip in the *smallest* units of the currency (integer, **not** float/double). At most 4 suggested tip amounts can be specified. The suggested tip amounts must be @@ -146,19 +155,19 @@ class InputInvoiceMessageContent(InputMessageContent): photo_width (:obj:`int`): Optional. Photo width. photo_height (:obj:`int`): Optional. Photo height. need_name (:obj:`bool`): Optional. Pass :obj:`True`, if you require the user's full name to - complete the order. + complete the order. Ignored for payments in |tg_stars|. need_phone_number (:obj:`bool`): Optional. Pass :obj:`True`, if you require the user's - phone number to complete the order + phone number to complete the order. Ignored for payments in |tg_stars|. need_email (:obj:`bool`): Optional. Pass :obj:`True`, if you require the user's email - address to complete the order. + address to complete the order. Ignored for payments in |tg_stars|. need_shipping_address (:obj:`bool`): Optional. Pass :obj:`True`, if you require the user's - shipping address to complete the order + shipping address to complete the order. Ignored for payments in |tg_stars|. send_phone_number_to_provider (:obj:`bool`): Optional. Pass :obj:`True`, if user's phone - number should be sent to provider. + number should be sent to provider. Ignored for payments in |tg_stars|. send_email_to_provider (:obj:`bool`): Optional. Pass :obj:`True`, if user's email address - should be sent to provider. + should be sent to provider. Ignored for payments in |tg_stars|. is_flexible (:obj:`bool`): Optional. Pass :obj:`True`, if the final price depends on the - shipping method. + shipping method. Ignored for payments in |tg_stars|. """ @@ -190,7 +199,7 @@ def __init__( title: str, description: str, payload: str, - provider_token: str, + provider_token: Optional[str], # This arg is now optional since Bot API 7.4 currency: str, prices: Sequence[LabeledPrice], max_tip_amount: Optional[int] = None, @@ -216,7 +225,7 @@ def __init__( self.title: str = title self.description: str = description self.payload: str = payload - self.provider_token: str = provider_token + self.provider_token: Optional[str] = provider_token self.currency: str = currency self.prices: Tuple[LabeledPrice, ...] = parse_sequence_arg(prices) # Optionals diff --git a/telegram/_keyboardbutton.py b/telegram/_keyboardbutton.py index ad69a176137..0cb4cd82e65 100644 --- a/telegram/_keyboardbutton.py +++ b/telegram/_keyboardbutton.py @@ -32,7 +32,8 @@ class KeyboardButton(TelegramObject): """ - This object represents one button of the reply keyboard. For simple text buttons, :obj:`str` + This object represents one button of the reply keyboard. At most one of the optional fields + must be used to specify type of the button. For simple text buttons, :obj:`str` can be used instead of this object to specify text of the button. Objects of this class are comparable in terms of equality. Two objects of this class are diff --git a/telegram/_message.py b/telegram/_message.py index 195724d36f7..43b7519b5bf 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -328,6 +328,11 @@ class Message(MaybeInaccessibleMessage): .. versionadded:: 20.8 + effect_id (:obj:`str`, optional): Unique identifier of the message effect added to the + message. + + ..versionadded:: NEXT.VERSION + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): For messages with a Caption. Special entities like usernames, URLs, bot commands, etc. that appear in the caption. See :attr:`Message.parse_caption_entity` and :attr:`parse_caption_entities` @@ -337,6 +342,9 @@ class Message(MaybeInaccessibleMessage): .. versionchanged:: 20.0 |sequenceclassargs| + show_caption_above_media (:obj:`bool`, optional): |show_cap_above_med| + + .. versionadded:: NEXT.VERSION audio (:class:`telegram.Audio`, optional): Message is an audio file, information about the file. document (:class:`telegram.Document`, optional): Message is a general file, information @@ -617,6 +625,11 @@ class Message(MaybeInaccessibleMessage): .. versionadded:: 20.8 + effect_id (:obj:`str`): Optional. Unique identifier of the message effect added to the + message. + + ..versionadded:: NEXT.VERSION + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. For messages with a Caption. Special entities like usernames, URLs, bot commands, etc. that appear in the caption. See :attr:`Message.parse_caption_entity` and :attr:`parse_caption_entities` @@ -626,6 +639,9 @@ class Message(MaybeInaccessibleMessage): .. versionchanged:: 20.0 |tupleclassattrs| + show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| + + .. versionadded:: NEXT.VERSION audio (:class:`telegram.Audio`): Optional. Message is an audio file, information about the file. @@ -897,6 +913,7 @@ class Message(MaybeInaccessibleMessage): "dice", "document", "edit_date", + "effect_id", "entities", "external_reply", "forum_topic_closed", @@ -942,6 +959,7 @@ class Message(MaybeInaccessibleMessage): "sender_boost_count", "sender_business_bot", "sender_chat", + "show_caption_above_media", "sticker", "story", "successful_payment", @@ -1044,6 +1062,8 @@ def __init__( sender_business_bot: Optional[User] = None, is_from_offline: Optional[bool] = None, chat_background_set: Optional[ChatBackground] = None, + effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, api_kwargs: Optional[JSONDict] = None, ): @@ -1143,6 +1163,8 @@ def __init__( self.sender_business_bot: Optional[User] = sender_business_bot self.is_from_offline: Optional[bool] = is_from_offline self.chat_background_set: Optional[ChatBackground] = chat_background_set + self.effect_id: Optional[str] = effect_id + self.show_caption_above_media: Optional[bool] = show_caption_above_media self._effective_attachment = DEFAULT_NONE @@ -1634,6 +1656,7 @@ async def reply_text( message_thread_id: ODVInput[int] = DEFAULT_NONE, link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1698,6 +1721,7 @@ async def reply_text( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_markdown( @@ -1710,6 +1734,7 @@ async def reply_markdown( message_thread_id: ODVInput[int] = DEFAULT_NONE, link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1780,6 +1805,7 @@ async def reply_markdown( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_markdown_v2( @@ -1792,6 +1818,7 @@ async def reply_markdown_v2( message_thread_id: ODVInput[int] = DEFAULT_NONE, link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1858,6 +1885,7 @@ async def reply_markdown_v2( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_html( @@ -1870,6 +1898,7 @@ async def reply_html( message_thread_id: ODVInput[int] = DEFAULT_NONE, link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1936,6 +1965,7 @@ async def reply_html( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_media_group( @@ -1947,6 +1977,7 @@ async def reply_media_group( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: ODVInput[int] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2013,6 +2044,7 @@ async def reply_media_group( parse_mode=parse_mode, caption_entities=caption_entities, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_photo( @@ -2027,6 +2059,8 @@ async def reply_photo( message_thread_id: ODVInput[int] = DEFAULT_NONE, has_spoiler: Optional[bool] = None, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2092,6 +2126,8 @@ async def reply_photo( api_kwargs=api_kwargs, has_spoiler=has_spoiler, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, ) async def reply_audio( @@ -2109,6 +2145,7 @@ async def reply_audio( message_thread_id: ODVInput[int] = DEFAULT_NONE, thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2177,6 +2214,7 @@ async def reply_audio( api_kwargs=api_kwargs, thumbnail=thumbnail, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_document( @@ -2192,6 +2230,7 @@ async def reply_document( message_thread_id: ODVInput[int] = DEFAULT_NONE, thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2258,6 +2297,7 @@ async def reply_document( message_thread_id=message_thread_id, thumbnail=thumbnail, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_animation( @@ -2276,6 +2316,8 @@ async def reply_animation( has_spoiler: Optional[bool] = None, thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2345,6 +2387,8 @@ async def reply_animation( has_spoiler=has_spoiler, thumbnail=thumbnail, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, ) async def reply_sticker( @@ -2356,6 +2400,7 @@ async def reply_sticker( message_thread_id: ODVInput[int] = DEFAULT_NONE, emoji: Optional[str] = None, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2416,6 +2461,7 @@ async def reply_sticker( message_thread_id=message_thread_id, emoji=emoji, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_video( @@ -2435,6 +2481,8 @@ async def reply_video( has_spoiler: Optional[bool] = None, thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2505,6 +2553,8 @@ async def reply_video( has_spoiler=has_spoiler, thumbnail=thumbnail, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, ) async def reply_video_note( @@ -2518,6 +2568,7 @@ async def reply_video_note( message_thread_id: ODVInput[int] = DEFAULT_NONE, thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2582,6 +2633,7 @@ async def reply_video_note( message_thread_id=message_thread_id, thumbnail=thumbnail, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_voice( @@ -2596,6 +2648,7 @@ async def reply_voice( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: ODVInput[int] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2661,6 +2714,7 @@ async def reply_voice( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_location( @@ -2676,6 +2730,7 @@ async def reply_location( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: ODVInput[int] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2742,6 +2797,7 @@ async def reply_location( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_venue( @@ -2759,6 +2815,7 @@ async def reply_venue( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: ODVInput[int] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2827,6 +2884,7 @@ async def reply_venue( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_contact( @@ -2840,6 +2898,7 @@ async def reply_contact( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: ODVInput[int] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2904,6 +2963,7 @@ async def reply_contact( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_poll( @@ -2927,6 +2987,7 @@ async def reply_poll( reply_parameters: Optional["ReplyParameters"] = None, question_parse_mode: ODVInput[str] = DEFAULT_NONE, question_entities: Optional[Sequence["MessageEntity"]] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2999,6 +3060,7 @@ async def reply_poll( business_connection_id=self.business_connection_id, question_parse_mode=question_parse_mode, question_entities=question_entities, + message_effect_id=message_effect_id, ) async def reply_dice( @@ -3009,6 +3071,7 @@ async def reply_dice( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: ODVInput[int] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -3068,6 +3131,7 @@ async def reply_dice( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_chat_action( @@ -3122,6 +3186,7 @@ async def reply_game( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: ODVInput[int] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -3183,6 +3248,7 @@ async def reply_game( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=self.business_connection_id, + message_effect_id=message_effect_id, ) async def reply_invoice( @@ -3190,7 +3256,7 @@ async def reply_invoice( title: str, description: str, payload: str, - provider_token: str, + provider_token: Optional[str], currency: str, prices: Sequence["LabeledPrice"], start_parameter: Optional[str] = None, @@ -3213,6 +3279,7 @@ async def reply_invoice( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: ODVInput[int] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -3302,6 +3369,7 @@ async def reply_invoice( suggested_tip_amounts=suggested_tip_amounts, protect_content=protect_content, message_thread_id=message_thread_id, + message_effect_id=message_effect_id, ) async def forward( @@ -3365,6 +3433,7 @@ async def copy( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -3409,6 +3478,7 @@ async def copy( api_kwargs=api_kwargs, protect_content=protect_content, message_thread_id=message_thread_id, + show_caption_above_media=show_caption_above_media, ) async def reply_copy( @@ -3423,6 +3493,7 @@ async def reply_copy( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: ODVInput[int] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -3486,6 +3557,7 @@ async def reply_copy( api_kwargs=api_kwargs, protect_content=protect_content, message_thread_id=message_thread_id, + show_caption_above_media=show_caption_above_media, ) async def edit_text( @@ -3544,6 +3616,7 @@ async def edit_caption( reply_markup: Optional["InlineKeyboardMarkup"] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence["MessageEntity"]] = None, + show_caption_above_media: Optional[bool] = None, *, read_timeout: ODVInput[float] = DEFAULT_NONE, write_timeout: ODVInput[float] = DEFAULT_NONE, @@ -3583,6 +3656,7 @@ async def edit_caption( api_kwargs=api_kwargs, caption_entities=caption_entities, inline_message_id=None, + show_caption_above_media=show_caption_above_media, ) async def edit_media( @@ -4333,6 +4407,8 @@ def _parse_html( insert = f'{escaped_text}' elif entity.type == MessageEntity.BLOCKQUOTE: insert = f"
{escaped_text}
" + elif entity.type == MessageEntity.EXPANDABLE_BLOCKQUOTE: + insert = f"
{escaped_text}
" elif entity.type == MessageEntity.BOLD: insert = f"{escaped_text}" elif entity.type == MessageEntity.ITALIC: @@ -4483,11 +4559,12 @@ def _parse_markdown( ) -> Optional[str]: if version == 1: for entity_type in ( - MessageEntity.UNDERLINE, - MessageEntity.STRIKETHROUGH, - MessageEntity.SPOILER, + MessageEntity.EXPANDABLE_BLOCKQUOTE, MessageEntity.BLOCKQUOTE, MessageEntity.CUSTOM_EMOJI, + MessageEntity.SPOILER, + MessageEntity.STRIKETHROUGH, + MessageEntity.UNDERLINE, ): if any(entity.type == entity_type for entity in entities): name = entity_type.name.title().replace("_", " ") # type:ignore[attr-defined] @@ -4567,8 +4644,10 @@ def _parse_markdown( insert = f"~{escaped_text}~" elif entity.type == MessageEntity.SPOILER: insert = f"||{escaped_text}||" - elif entity.type == MessageEntity.BLOCKQUOTE: + elif entity.type in (MessageEntity.BLOCKQUOTE, MessageEntity.EXPANDABLE_BLOCKQUOTE): insert = ">" + "\n>".join(escaped_text.splitlines()) + if entity.type == MessageEntity.EXPANDABLE_BLOCKQUOTE: + insert = f"{insert}||" elif entity.type == MessageEntity.CUSTOM_EMOJI: # This should never be needed because ids are numeric but the documentation # specifically mentions it so here we are diff --git a/telegram/_messageentity.py b/telegram/_messageentity.py index f6e0ba6edfe..ebdcb8e077c 100644 --- a/telegram/_messageentity.py +++ b/telegram/_messageentity.py @@ -140,50 +140,55 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MessageEntit return super().de_json(data=data, bot=bot) - MENTION: Final[str] = constants.MessageEntityType.MENTION - """:const:`telegram.constants.MessageEntityType.MENTION`""" - HASHTAG: Final[str] = constants.MessageEntityType.HASHTAG - """:const:`telegram.constants.MessageEntityType.HASHTAG`""" - CASHTAG: Final[str] = constants.MessageEntityType.CASHTAG - """:const:`telegram.constants.MessageEntityType.CASHTAG`""" - PHONE_NUMBER: Final[str] = constants.MessageEntityType.PHONE_NUMBER - """:const:`telegram.constants.MessageEntityType.PHONE_NUMBER`""" + ALL_TYPES: Final[List[str]] = list(constants.MessageEntityType) + """List[:obj:`str`]: A list of all available message entity types.""" + BLOCKQUOTE: Final[str] = constants.MessageEntityType.BLOCKQUOTE + """:const:`telegram.constants.MessageEntityType.BLOCKQUOTE` + + .. versionadded:: 20.8 + """ + BOLD: Final[str] = constants.MessageEntityType.BOLD + """:const:`telegram.constants.MessageEntityType.BOLD`""" BOT_COMMAND: Final[str] = constants.MessageEntityType.BOT_COMMAND """:const:`telegram.constants.MessageEntityType.BOT_COMMAND`""" - URL: Final[str] = constants.MessageEntityType.URL - """:const:`telegram.constants.MessageEntityType.URL`""" + CASHTAG: Final[str] = constants.MessageEntityType.CASHTAG + """:const:`telegram.constants.MessageEntityType.CASHTAG`""" + CODE: Final[str] = constants.MessageEntityType.CODE + """:const:`telegram.constants.MessageEntityType.CODE`""" + CUSTOM_EMOJI: Final[str] = constants.MessageEntityType.CUSTOM_EMOJI + """:const:`telegram.constants.MessageEntityType.CUSTOM_EMOJI` + + .. versionadded:: 20.0 + """ EMAIL: Final[str] = constants.MessageEntityType.EMAIL """:const:`telegram.constants.MessageEntityType.EMAIL`""" - BOLD: Final[str] = constants.MessageEntityType.BOLD - """:const:`telegram.constants.MessageEntityType.BOLD`""" + EXPANDABLE_BLOCKQUOTE: Final[str] = constants.MessageEntityType.EXPANDABLE_BLOCKQUOTE + """:const:`telegram.constants.MessageEntityType.EXPANDABLE_BLOCKQUOTE` + + .. versionadded:: NEXT.VERSION + """ + HASHTAG: Final[str] = constants.MessageEntityType.HASHTAG + """:const:`telegram.constants.MessageEntityType.HASHTAG`""" ITALIC: Final[str] = constants.MessageEntityType.ITALIC """:const:`telegram.constants.MessageEntityType.ITALIC`""" - CODE: Final[str] = constants.MessageEntityType.CODE - """:const:`telegram.constants.MessageEntityType.CODE`""" + MENTION: Final[str] = constants.MessageEntityType.MENTION + """:const:`telegram.constants.MessageEntityType.MENTION`""" + PHONE_NUMBER: Final[str] = constants.MessageEntityType.PHONE_NUMBER + """:const:`telegram.constants.MessageEntityType.PHONE_NUMBER`""" PRE: Final[str] = constants.MessageEntityType.PRE """:const:`telegram.constants.MessageEntityType.PRE`""" + SPOILER: Final[str] = constants.MessageEntityType.SPOILER + """:const:`telegram.constants.MessageEntityType.SPOILER` + + .. versionadded:: 13.10 + """ + STRIKETHROUGH: Final[str] = constants.MessageEntityType.STRIKETHROUGH + """:const:`telegram.constants.MessageEntityType.STRIKETHROUGH`""" TEXT_LINK: Final[str] = constants.MessageEntityType.TEXT_LINK """:const:`telegram.constants.MessageEntityType.TEXT_LINK`""" TEXT_MENTION: Final[str] = constants.MessageEntityType.TEXT_MENTION """:const:`telegram.constants.MessageEntityType.TEXT_MENTION`""" UNDERLINE: Final[str] = constants.MessageEntityType.UNDERLINE """:const:`telegram.constants.MessageEntityType.UNDERLINE`""" - STRIKETHROUGH: Final[str] = constants.MessageEntityType.STRIKETHROUGH - """:const:`telegram.constants.MessageEntityType.STRIKETHROUGH`""" - SPOILER: Final[str] = constants.MessageEntityType.SPOILER - """:const:`telegram.constants.MessageEntityType.SPOILER` - - .. versionadded:: 13.10 - """ - CUSTOM_EMOJI: Final[str] = constants.MessageEntityType.CUSTOM_EMOJI - """:const:`telegram.constants.MessageEntityType.CUSTOM_EMOJI` - - .. versionadded:: 20.0 - """ - BLOCKQUOTE: Final[str] = constants.MessageEntityType.BLOCKQUOTE - """:const:`telegram.constants.MessageEntityType.BLOCKQUOTE` - - .. versionadded:: 20.8 - """ - ALL_TYPES: Final[List[str]] = list(constants.MessageEntityType) - """List[:obj:`str`]: A list of all available message entity types.""" + URL: Final[str] = constants.MessageEntityType.URL + """:const:`telegram.constants.MessageEntityType.URL`""" diff --git a/telegram/_payment/invoice.py b/telegram/_payment/invoice.py index 04878424698..141f0c8fdd4 100644 --- a/telegram/_payment/invoice.py +++ b/telegram/_payment/invoice.py @@ -37,7 +37,8 @@ class Invoice(TelegramObject): description (:obj:`str`): Product description. start_parameter (:obj:`str`): Unique bot deep-linking parameter that can be used to generate this invoice. - currency (:obj:`str`): Three-letter ISO 4217 currency code. + currency (:obj:`str`): Three-letter ISO 4217 currency code, or ``XTR`` for payments in + |tg_stars|. total_amount (:obj:`int`): Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass ``amount = 145``. See the ``exp`` parameter in @@ -50,7 +51,8 @@ class Invoice(TelegramObject): description (:obj:`str`): Product description. start_parameter (:obj:`str`): Unique bot deep-linking parameter that can be used to generate this invoice. - currency (:obj:`str`): Three-letter ISO 4217 currency code. + currency (:obj:`str`): Three-letter ISO 4217 currency code, or ``XTR`` for payments in + |tg_stars|. total_amount (:obj:`int`): Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 ``amount`` is ``145``. See the ``exp`` parameter in diff --git a/telegram/_payment/precheckoutquery.py b/telegram/_payment/precheckoutquery.py index 4e7127eea07..fb57127fc11 100644 --- a/telegram/_payment/precheckoutquery.py +++ b/telegram/_payment/precheckoutquery.py @@ -42,7 +42,8 @@ class PreCheckoutQuery(TelegramObject): Args: id (:obj:`str`): Unique query identifier. from_user (:class:`telegram.User`): User who sent the query. - currency (:obj:`str`): Three-letter ISO 4217 currency code. + currency (:obj:`str`): Three-letter ISO 4217 currency code, or ``XTR`` for payments in + |tg_stars|. total_amount (:obj:`int`): Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass ``amount = 145``. See the ``exp`` parameter in @@ -57,7 +58,8 @@ class PreCheckoutQuery(TelegramObject): Attributes: id (:obj:`str`): Unique query identifier. from_user (:class:`telegram.User`): User who sent the query. - currency (:obj:`str`): Three-letter ISO 4217 currency code. + currency (:obj:`str`): Three-letter ISO 4217 currency code, or ``XTR`` for payments in + |tg_stars|. total_amount (:obj:`int`): Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 ``amount`` is ``145``. See the ``exp`` parameter in diff --git a/telegram/_payment/successfulpayment.py b/telegram/_payment/successfulpayment.py index 737ad841e55..a7424feba22 100644 --- a/telegram/_payment/successfulpayment.py +++ b/telegram/_payment/successfulpayment.py @@ -36,7 +36,8 @@ class SuccessfulPayment(TelegramObject): :attr:`provider_payment_charge_id` are equal. Args: - currency (:obj:`str`): Three-letter ISO 4217 currency code. + currency (:obj:`str`): Three-letter ISO 4217 currency code, or ``XTR`` for payments in + |tg_stars|. total_amount (:obj:`int`): Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass ``amount = 145``. See the ``exp`` parameter in @@ -51,7 +52,8 @@ class SuccessfulPayment(TelegramObject): provider_payment_charge_id (:obj:`str`): Provider payment identifier. Attributes: - currency (:obj:`str`): Three-letter ISO 4217 currency code. + currency (:obj:`str`): Three-letter ISO 4217 currency code, or ``XTR`` for payments in + |tg_stars|. total_amount (:obj:`int`): Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 ``amount`` is ``145``. See the ``exp`` parameter in diff --git a/telegram/_user.py b/telegram/_user.py index 17b58f2df6f..84ab7728cee 100644 --- a/telegram/_user.py +++ b/telegram/_user.py @@ -409,6 +409,7 @@ async def send_message( link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, disable_web_page_preview: Optional[bool] = None, @@ -452,6 +453,7 @@ async def send_message( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def delete_message( @@ -531,6 +533,8 @@ async def send_photo( has_spoiler: Optional[bool] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -575,6 +579,8 @@ async def send_photo( api_kwargs=api_kwargs, has_spoiler=has_spoiler, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, ) async def send_media_group( @@ -587,6 +593,7 @@ async def send_media_group( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -631,6 +638,7 @@ async def send_media_group( parse_mode=parse_mode, caption_entities=caption_entities, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_audio( @@ -649,6 +657,7 @@ async def send_audio( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -696,6 +705,7 @@ async def send_audio( api_kwargs=api_kwargs, thumbnail=thumbnail, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_chat_action( @@ -750,6 +760,7 @@ async def send_contact( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -793,6 +804,7 @@ async def send_contact( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_dice( @@ -804,6 +816,7 @@ async def send_dice( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -842,6 +855,7 @@ async def send_dice( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_document( @@ -858,6 +872,7 @@ async def send_document( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -903,6 +918,7 @@ async def send_document( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_game( @@ -914,6 +930,7 @@ async def send_game( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -952,6 +969,7 @@ async def send_game( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_invoice( @@ -959,7 +977,7 @@ async def send_invoice( title: str, description: str, payload: str, - provider_token: str, + provider_token: Optional[str], currency: str, prices: Sequence["LabeledPrice"], start_parameter: Optional[str] = None, @@ -982,6 +1000,7 @@ async def send_invoice( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1049,6 +1068,7 @@ async def send_invoice( suggested_tip_amounts=suggested_tip_amounts, protect_content=protect_content, message_thread_id=message_thread_id, + message_effect_id=message_effect_id, ) async def send_location( @@ -1065,6 +1085,7 @@ async def send_location( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1110,6 +1131,7 @@ async def send_location( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_animation( @@ -1129,6 +1151,8 @@ async def send_animation( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1177,6 +1201,8 @@ async def send_animation( has_spoiler=has_spoiler, thumbnail=thumbnail, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, ) async def send_sticker( @@ -1189,6 +1215,7 @@ async def send_sticker( emoji: Optional[str] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1228,6 +1255,7 @@ async def send_sticker( message_thread_id=message_thread_id, emoji=emoji, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_video( @@ -1248,6 +1276,8 @@ async def send_video( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1297,6 +1327,8 @@ async def send_video( message_thread_id=message_thread_id, has_spoiler=has_spoiler, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, ) async def send_venue( @@ -1315,6 +1347,7 @@ async def send_venue( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1362,6 +1395,7 @@ async def send_venue( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_video_note( @@ -1376,6 +1410,7 @@ async def send_video_note( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1419,6 +1454,7 @@ async def send_video_note( message_thread_id=message_thread_id, thumbnail=thumbnail, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_voice( @@ -1434,6 +1470,7 @@ async def send_voice( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1478,6 +1515,7 @@ async def send_voice( protect_content=protect_content, message_thread_id=message_thread_id, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_poll( @@ -1502,6 +1540,7 @@ async def send_poll( business_connection_id: Optional[str] = None, question_parse_mode: ODVInput[str] = DEFAULT_NONE, question_entities: Optional[Sequence["MessageEntity"]] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1553,6 +1592,7 @@ async def send_poll( business_connection_id=business_connection_id, question_parse_mode=question_parse_mode, question_entities=question_entities, + message_effect_id=message_effect_id, ) async def send_copy( @@ -1567,6 +1607,7 @@ async def send_copy( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1608,6 +1649,7 @@ async def send_copy( api_kwargs=api_kwargs, protect_content=protect_content, message_thread_id=message_thread_id, + show_caption_above_media=show_caption_above_media, ) async def copy_message( @@ -1622,6 +1664,7 @@ async def copy_message( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -1663,6 +1706,7 @@ async def copy_message( api_kwargs=api_kwargs, protect_content=protect_content, message_thread_id=message_thread_id, + show_caption_above_media=show_caption_above_media, ) async def send_copies( @@ -2101,3 +2145,35 @@ async def get_chat_boosts( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) + + async def refund_star_payment( + self, + telegram_payment_charge_id: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + ) -> bool: + """Shortcut for:: + + await bot.refund_star_payment(user_id=update.effective_user.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.refund_star_payment`. + + .. versionadded:: NEXT.VERSION + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().refund_star_payment( + user_id=self.id, + telegram_payment_charge_id=telegram_payment_charge_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) diff --git a/telegram/constants.py b/telegram/constants.py index 06f5bff86f5..faf797231a8 100644 --- a/telegram/constants.py +++ b/telegram/constants.py @@ -146,7 +146,7 @@ class _AccentColor(NamedTuple): #: :data:`telegram.__bot_api_version_info__`. #: #: .. versionadded:: 20.0 -BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=3) +BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=4) #: :obj:`str`: Telegram Bot API #: version supported by this version of `python-telegram-bot`. Also available as #: :data:`telegram.__bot_api_version__`. @@ -1630,48 +1630,53 @@ class MessageEntityType(StringEnum): __slots__ = () - MENTION = "mention" - """:obj:`str`: Message entities representing a mention.""" - HASHTAG = "hashtag" - """:obj:`str`: Message entities representing a hashtag.""" - CASHTAG = "cashtag" - """:obj:`str`: Message entities representing a cashtag.""" - PHONE_NUMBER = "phone_number" - """:obj:`str`: Message entities representing a phone number.""" + BLOCKQUOTE = "blockquote" + """:obj:`str`: Message entities representing a block quotation. + + .. versionadded:: 20.8 + """ + BOLD = "bold" + """:obj:`str`: Message entities representing bold text.""" BOT_COMMAND = "bot_command" """:obj:`str`: Message entities representing a bot command.""" - URL = "url" - """:obj:`str`: Message entities representing a url.""" + CASHTAG = "cashtag" + """:obj:`str`: Message entities representing a cashtag.""" + CODE = "code" + """:obj:`str`: Message entities representing monowidth string.""" + CUSTOM_EMOJI = "custom_emoji" + """:obj:`str`: Message entities representing inline custom emoji stickers. + + .. versionadded:: 20.0 + """ EMAIL = "email" """:obj:`str`: Message entities representing a email.""" - BOLD = "bold" - """:obj:`str`: Message entities representing bold text.""" + EXPANDABLE_BLOCKQUOTE = "expandable_blockquote" + """:obj:`str`: Message entities representing collapsed-by-default block quotation. + + .. versionadded:: NEXT.VERSION + """ + HASHTAG = "hashtag" + """:obj:`str`: Message entities representing a hashtag.""" ITALIC = "italic" """:obj:`str`: Message entities representing italic text.""" - CODE = "code" - """:obj:`str`: Message entities representing monowidth string.""" + MENTION = "mention" + """:obj:`str`: Message entities representing a mention.""" + PHONE_NUMBER = "phone_number" + """:obj:`str`: Message entities representing a phone number.""" PRE = "pre" """:obj:`str`: Message entities representing monowidth block.""" + SPOILER = "spoiler" + """:obj:`str`: Message entities representing spoiler text.""" + STRIKETHROUGH = "strikethrough" + """:obj:`str`: Message entities representing strikethrough text.""" TEXT_LINK = "text_link" """:obj:`str`: Message entities representing clickable text URLs.""" TEXT_MENTION = "text_mention" """:obj:`str`: Message entities representing text mention for users without usernames.""" UNDERLINE = "underline" """:obj:`str`: Message entities representing underline text.""" - STRIKETHROUGH = "strikethrough" - """:obj:`str`: Message entities representing strikethrough text.""" - SPOILER = "spoiler" - """:obj:`str`: Message entities representing spoiler text.""" - CUSTOM_EMOJI = "custom_emoji" - """:obj:`str`: Message entities representing inline custom emoji stickers. - - .. versionadded:: 20.0 - """ - BLOCKQUOTE = "blockquote" - """:obj:`str`: Message entities representing a block quotation. - - .. versionadded:: 20.8 - """ + URL = "url" + """:obj:`str`: Message entities representing a url.""" class MessageLimit(IntEnum): @@ -1801,6 +1806,10 @@ class MessageType(StringEnum): """:obj:`str`: Messages with :attr:`telegram.Message.dice`.""" DOCUMENT = "document" """:obj:`str`: Messages with :attr:`telegram.Message.document`.""" + EFFECT_ID = "effect_id" + """:obj:`str`: Messages with :attr:`telegram.Message.effect_id`. + + .. versionadded:: NEXT.VERSION""" FORUM_TOPIC_CREATED = "forum_topic_created" """:obj:`str`: Messages with :attr:`telegram.Message.forum_topic_created`. diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 6cefee43c18..917e9d8ef97 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -593,6 +593,7 @@ async def _send_message( link_preview_options: ODVInput["LinkPreviewOptions"] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -624,6 +625,7 @@ async def _send_message( pool_timeout=pool_timeout, api_kwargs=api_kwargs, business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) if isinstance(result, Message): self._insert_callback_data(result) @@ -798,6 +800,7 @@ async def copy_message( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -828,6 +831,7 @@ async def copy_message( connect_timeout=connect_timeout, pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + show_caption_above_media=show_caption_above_media, ) async def copy_messages( @@ -1146,7 +1150,7 @@ async def create_invoice_link( title: str, description: str, payload: str, - provider_token: str, + provider_token: Optional[str], currency: str, prices: Sequence["LabeledPrice"], max_tip_amount: Optional[int] = None, @@ -1506,6 +1510,7 @@ async def edit_message_caption( reply_markup: Optional["InlineKeyboardMarkup"] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence["MessageEntity"]] = None, + show_caption_above_media: Optional[bool] = None, *, read_timeout: ODVInput[float] = DEFAULT_NONE, write_timeout: ODVInput[float] = DEFAULT_NONE, @@ -1527,6 +1532,7 @@ async def edit_message_caption( connect_timeout=connect_timeout, pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + show_caption_above_media=show_caption_above_media, ) async def edit_message_live_location( @@ -2379,6 +2385,8 @@ async def send_animation( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2415,6 +2423,8 @@ async def send_animation( business_connection_id=business_connection_id, pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, ) async def send_audio( @@ -2434,6 +2444,7 @@ async def send_audio( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2469,6 +2480,7 @@ async def send_audio( connect_timeout=connect_timeout, pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + message_effect_id=message_effect_id, ) async def send_chat_action( @@ -2510,6 +2522,7 @@ async def send_contact( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2541,6 +2554,7 @@ async def send_contact( pool_timeout=pool_timeout, business_connection_id=business_connection_id, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + message_effect_id=message_effect_id, ) async def send_dice( @@ -2553,6 +2567,7 @@ async def send_dice( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2579,6 +2594,7 @@ async def send_dice( connect_timeout=connect_timeout, pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + message_effect_id=message_effect_id, ) async def send_document( @@ -2596,6 +2612,7 @@ async def send_document( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2629,6 +2646,7 @@ async def send_document( connect_timeout=connect_timeout, pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + message_effect_id=message_effect_id, ) async def send_game( @@ -2641,6 +2659,7 @@ async def send_game( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2667,6 +2686,7 @@ async def send_game( connect_timeout=connect_timeout, pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + message_effect_id=message_effect_id, ) async def send_invoice( @@ -2675,7 +2695,7 @@ async def send_invoice( title: str, description: str, payload: str, - provider_token: str, + provider_token: Optional[str], currency: str, prices: Sequence["LabeledPrice"], start_parameter: Optional[str] = None, @@ -2698,6 +2718,7 @@ async def send_invoice( protect_content: ODVInput[bool] = DEFAULT_NONE, message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2743,6 +2764,7 @@ async def send_invoice( connect_timeout=connect_timeout, pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + message_effect_id=message_effect_id, ) async def send_location( @@ -2760,6 +2782,7 @@ async def send_location( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2793,6 +2816,7 @@ async def send_location( business_connection_id=business_connection_id, pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + message_effect_id=message_effect_id, ) async def send_media_group( @@ -2806,6 +2830,7 @@ async def send_media_group( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2837,6 +2862,7 @@ async def send_media_group( business_connection_id=business_connection_id, parse_mode=parse_mode, caption_entities=caption_entities, + message_effect_id=message_effect_id, ) async def send_message( @@ -2852,6 +2878,7 @@ async def send_message( link_preview_options: ODVInput["LinkPreviewOptions"] = DEFAULT_NONE, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, disable_web_page_preview: Optional[bool] = None, reply_to_message_id: Optional[int] = None, @@ -2883,6 +2910,7 @@ async def send_message( pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), link_preview_options=link_preview_options, + message_effect_id=message_effect_id, ) async def send_photo( @@ -2899,6 +2927,8 @@ async def send_photo( has_spoiler: Optional[bool] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2931,6 +2961,8 @@ async def send_photo( connect_timeout=connect_timeout, pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, ) async def send_poll( @@ -2956,6 +2988,7 @@ async def send_poll( business_connection_id: Optional[str] = None, question_parse_mode: ODVInput[str] = DEFAULT_NONE, question_entities: Optional[Sequence["MessageEntity"]] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -2995,6 +3028,7 @@ async def send_poll( api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), question_parse_mode=question_parse_mode, question_entities=question_entities, + message_effect_id=message_effect_id, ) async def send_sticker( @@ -3008,6 +3042,7 @@ async def send_sticker( emoji: Optional[str] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -3035,6 +3070,7 @@ async def send_sticker( pool_timeout=pool_timeout, emoji=emoji, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + message_effect_id=message_effect_id, ) async def send_venue( @@ -3054,6 +3090,7 @@ async def send_venue( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -3089,6 +3126,7 @@ async def send_venue( connect_timeout=connect_timeout, pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + message_effect_id=message_effect_id, ) async def send_video( @@ -3110,6 +3148,8 @@ async def send_video( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, + show_caption_above_media: Optional[bool] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -3147,6 +3187,8 @@ async def send_video( connect_timeout=connect_timeout, pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media, ) async def send_video_note( @@ -3162,6 +3204,7 @@ async def send_video_note( thumbnail: Optional[FileInput] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -3193,6 +3236,7 @@ async def send_video_note( pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def send_voice( @@ -3209,6 +3253,7 @@ async def send_voice( message_thread_id: Optional[int] = None, reply_parameters: Optional["ReplyParameters"] = None, business_connection_id: Optional[str] = None, + message_effect_id: Optional[str] = None, *, reply_to_message_id: Optional[int] = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, @@ -3241,6 +3286,7 @@ async def send_voice( pool_timeout=pool_timeout, api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), business_connection_id=business_connection_id, + message_effect_id=message_effect_id, ) async def set_chat_administrator_custom_title( @@ -4111,6 +4157,28 @@ async def replace_sticker_in_set( api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), ) + async def refund_star_payment( + self, + user_id: int, + telegram_payment_charge_id: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: Optional[JSONDict] = None, + rate_limit_args: Optional[RLARGS] = None, + ) -> bool: + return await super().refund_star_payment( + user_id=user_id, + telegram_payment_charge_id=telegram_payment_charge_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + ) + # updated camelCase aliases getMe = get_me sendMessage = send_message @@ -4232,3 +4300,4 @@ async def replace_sticker_in_set( setMessageReaction = set_message_reaction getBusinessConnection = get_business_connection replaceStickerInSet = replace_sticker_in_set + refundStarPayment = refund_star_payment diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index 1fef40a5781..24dc982e653 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -46,6 +46,7 @@ "CHAT", "COMMAND", "CONTACT", + "EFFECT_ID", "FORWARDED", "GAME", "GIVEAWAY", @@ -1338,6 +1339,19 @@ def filter(self, message: Message) -> bool: """Use as ``filters.Document.ZIP``.""" +class _EffectId(MessageFilter): + __slots__ = () + + def filter(self, message: Message) -> bool: + return bool(message.effect_id) + + +EFFECT_ID = _EffectId(name="filters.EFFECT_ID") +"""Messages that contain :attr:`telegram.Message.effect_id`. + +.. versionadded:: NEXT.VERSION""" + + class Entity(MessageFilter): """ Filters messages to only allow those which have a :class:`telegram.MessageEntity` diff --git a/tests/_files/test_animation.py b/tests/_files/test_animation.py index f14504a75fd..74a17fca48a 100644 --- a/tests/_files/test_animation.py +++ b/tests/_files/test_animation.py @@ -233,6 +233,7 @@ async def test_send_all_args(self, bot, chat_id, animation_file, animation, thum protect_content=True, thumbnail=thumb_file, has_spoiler=True, + show_caption_above_media=True, ) assert isinstance(message.animation, Animation) @@ -246,6 +247,7 @@ async def test_send_all_args(self, bot, chat_id, animation_file, animation, thum assert message.animation.thumbnail.width == self.width assert message.animation.thumbnail.height == self.height assert message.has_protected_content + assert message.show_caption_above_media try: assert message.has_media_spoiler except AssertionError: diff --git a/tests/_files/test_inputmedia.py b/tests/_files/test_inputmedia.py index 6febe12c87f..cce7fdc07a5 100644 --- a/tests/_files/test_inputmedia.py +++ b/tests/_files/test_inputmedia.py @@ -76,6 +76,7 @@ def input_media_video(class_thumb_file): thumbnail=class_thumb_file, supports_streaming=TestInputMediaVideoBase.supports_streaming, has_spoiler=TestInputMediaVideoBase.has_spoiler, + show_caption_above_media=TestInputMediaVideoBase.show_caption_above_media, ) @@ -87,6 +88,7 @@ def input_media_photo(): parse_mode=TestInputMediaPhotoBase.parse_mode, caption_entities=TestInputMediaPhotoBase.caption_entities, has_spoiler=TestInputMediaPhotoBase.has_spoiler, + show_caption_above_media=TestInputMediaPhotoBase.show_caption_above_media, ) @@ -102,6 +104,7 @@ def input_media_animation(class_thumb_file): thumbnail=class_thumb_file, duration=TestInputMediaAnimationBase.duration, has_spoiler=TestInputMediaAnimationBase.has_spoiler, + show_caption_above_media=TestInputMediaAnimationBase.show_caption_above_media, ) @@ -142,6 +145,7 @@ class TestInputMediaVideoBase: supports_streaming = True caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)] has_spoiler = True + show_caption_above_media = True class TestInputMediaVideoWithoutRequest(TestInputMediaVideoBase): @@ -163,6 +167,7 @@ def test_expected_values(self, input_media_video): assert input_media_video.supports_streaming == self.supports_streaming assert isinstance(input_media_video.thumbnail, InputFile) assert input_media_video.has_spoiler == self.has_spoiler + assert input_media_video.show_caption_above_media == self.show_caption_above_media def test_caption_entities_always_tuple(self): input_media_video = InputMediaVideo(self.media) @@ -182,6 +187,10 @@ def test_to_dict(self, input_media_video): ] assert input_media_video_dict["supports_streaming"] == input_media_video.supports_streaming assert input_media_video_dict["has_spoiler"] == input_media_video.has_spoiler + assert ( + input_media_video_dict["show_caption_above_media"] + == input_media_video.show_caption_above_media + ) def test_with_video(self, video): # noqa: F811 # fixture found in test_video @@ -235,6 +244,7 @@ class TestInputMediaPhotoBase: parse_mode = "Markdown" caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)] has_spoiler = True + show_caption_above_media = True class TestInputMediaPhotoWithoutRequest(TestInputMediaPhotoBase): @@ -251,6 +261,7 @@ def test_expected_values(self, input_media_photo): assert input_media_photo.parse_mode == self.parse_mode assert input_media_photo.caption_entities == tuple(self.caption_entities) assert input_media_photo.has_spoiler == self.has_spoiler + assert input_media_photo.show_caption_above_media == self.show_caption_above_media def test_caption_entities_always_tuple(self): input_media_photo = InputMediaPhoto(self.media) @@ -266,6 +277,10 @@ def test_to_dict(self, input_media_photo): ce.to_dict() for ce in input_media_photo.caption_entities ] assert input_media_photo_dict["has_spoiler"] == input_media_photo.has_spoiler + assert ( + input_media_photo_dict["show_caption_above_media"] + == input_media_photo.show_caption_above_media + ) def test_with_photo(self, photo): # noqa: F811 # fixture found in test_photo @@ -296,6 +311,7 @@ class TestInputMediaAnimationBase: height = 30 duration = 1 has_spoiler = True + show_caption_above_media = True class TestInputMediaAnimationWithoutRequest(TestInputMediaAnimationBase): @@ -313,6 +329,7 @@ def test_expected_values(self, input_media_animation): assert input_media_animation.caption_entities == tuple(self.caption_entities) assert isinstance(input_media_animation.thumbnail, InputFile) assert input_media_animation.has_spoiler == self.has_spoiler + assert input_media_animation.show_caption_above_media == self.show_caption_above_media def test_caption_entities_always_tuple(self): input_media_animation = InputMediaAnimation(self.media) @@ -331,6 +348,10 @@ def test_to_dict(self, input_media_animation): assert input_media_animation_dict["height"] == input_media_animation.height assert input_media_animation_dict["duration"] == input_media_animation.duration assert input_media_animation_dict["has_spoiler"] == input_media_animation.has_spoiler + assert ( + input_media_animation_dict["show_caption_above_media"] + == input_media_animation.show_caption_above_media + ) def test_with_animation(self, animation): # noqa: F811 # fixture found in test_animation diff --git a/tests/_files/test_photo.py b/tests/_files/test_photo.py index d8be6e81473..ac53a048c27 100644 --- a/tests/_files/test_photo.py +++ b/tests/_files/test_photo.py @@ -247,6 +247,7 @@ async def test_send_photo_all_args(self, bot, chat_id, photo_file): protect_content=True, parse_mode="Markdown", has_spoiler=True, + show_caption_above_media=True, ) assert isinstance(message.photo[-2], PhotoSize) @@ -264,6 +265,7 @@ async def test_send_photo_all_args(self, bot, chat_id, photo_file): assert message.caption == self.caption.replace("*", "") assert message.has_protected_content assert message.has_media_spoiler + assert message.show_caption_above_media async def test_send_photo_parse_mode_markdown(self, bot, chat_id, photo_file): message = await bot.send_photo( diff --git a/tests/_files/test_video.py b/tests/_files/test_video.py index c7fedbb8cf0..7f31f9f6e4d 100644 --- a/tests/_files/test_video.py +++ b/tests/_files/test_video.py @@ -244,6 +244,7 @@ async def test_send_all_args(self, bot, chat_id, video_file, video, thumb_file): parse_mode="Markdown", thumbnail=thumb_file, has_spoiler=True, + show_caption_above_media=True, ) assert isinstance(message.video, Video) @@ -265,6 +266,7 @@ async def test_send_all_args(self, bot, chat_id, video_file, video, thumb_file): assert message.video.file_name == self.file_name assert message.has_protected_content assert message.has_media_spoiler + assert message.show_caption_above_media async def test_get_and_download(self, bot, video, chat_id, tmp_file): new_file = await bot.get_file(video.file_id) diff --git a/tests/_inline/test_inlinequeryresultcachedgif.py b/tests/_inline/test_inlinequeryresultcachedgif.py index d1a3fcdb85f..62b908a631f 100644 --- a/tests/_inline/test_inlinequeryresultcachedgif.py +++ b/tests/_inline/test_inlinequeryresultcachedgif.py @@ -40,6 +40,7 @@ def inline_query_result_cached_gif(): 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, ) @@ -53,6 +54,7 @@ class TestInlineQueryResultCachedGifBase: caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] input_message_content = InputTextMessageContent("input_message_content") reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) + show_caption_above_media = True class TestInlineQueryResultCachedGifWithoutRequest(TestInlineQueryResultCachedGifBase): @@ -75,6 +77,10 @@ def test_expected_values(self, inline_query_result_cached_gif): == self.input_message_content.to_dict() ) assert inline_query_result_cached_gif.reply_markup.to_dict() == self.reply_markup.to_dict() + assert ( + inline_query_result_cached_gif.show_caption_above_media + == self.show_caption_above_media + ) def test_caption_entities_always_tuple(self): result = InlineQueryResultCachedGif(self.id_, self.gif_file_id) @@ -110,6 +116,10 @@ def test_to_dict(self, inline_query_result_cached_gif): inline_query_result_cached_gif_dict["reply_markup"] == inline_query_result_cached_gif.reply_markup.to_dict() ) + assert ( + inline_query_result_cached_gif_dict["show_caption_above_media"] + == inline_query_result_cached_gif.show_caption_above_media + ) def test_equality(self): a = InlineQueryResultCachedGif(self.id_, self.gif_file_id) diff --git a/tests/_inline/test_inlinequeryresultcachedmpeg4gif.py b/tests/_inline/test_inlinequeryresultcachedmpeg4gif.py index 770b9d5c50c..3d4b5d3ca5b 100644 --- a/tests/_inline/test_inlinequeryresultcachedmpeg4gif.py +++ b/tests/_inline/test_inlinequeryresultcachedmpeg4gif.py @@ -40,6 +40,7 @@ def inline_query_result_cached_mpeg4_gif(): 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, ) @@ -53,6 +54,7 @@ class TestInlineQueryResultCachedMpeg4GifBase: caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] input_message_content = InputTextMessageContent("input_message_content") reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) + show_caption_above_media = True class TestInlineQueryResultCachedMpeg4GifWithoutRequest(TestInlineQueryResultCachedMpeg4GifBase): @@ -80,6 +82,10 @@ def test_expected_values(self, inline_query_result_cached_mpeg4_gif): inline_query_result_cached_mpeg4_gif.reply_markup.to_dict() == self.reply_markup.to_dict() ) + assert ( + inline_query_result_cached_mpeg4_gif.show_caption_above_media + == self.show_caption_above_media + ) def test_caption_entities_always_tuple(self): result = InlineQueryResultCachedMpeg4Gif(self.id_, self.mpeg4_file_id) @@ -124,6 +130,10 @@ def test_to_dict(self, inline_query_result_cached_mpeg4_gif): inline_query_result_cached_mpeg4_gif_dict["reply_markup"] == inline_query_result_cached_mpeg4_gif.reply_markup.to_dict() ) + assert ( + inline_query_result_cached_mpeg4_gif_dict["show_caption_above_media"] + == inline_query_result_cached_mpeg4_gif.show_caption_above_media + ) def test_equality(self): a = InlineQueryResultCachedMpeg4Gif(self.id_, self.mpeg4_file_id) diff --git a/tests/_inline/test_inlinequeryresultcachedphoto.py b/tests/_inline/test_inlinequeryresultcachedphoto.py index 02b8df1562d..b5ed6153b91 100644 --- a/tests/_inline/test_inlinequeryresultcachedphoto.py +++ b/tests/_inline/test_inlinequeryresultcachedphoto.py @@ -41,6 +41,7 @@ def inline_query_result_cached_photo(): 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, ) @@ -55,6 +56,7 @@ class TestInlineQueryResultCachedPhotoBase: caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] input_message_content = InputTextMessageContent("input_message_content") reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) + show_caption_above_media = True class TestInlineQueryResultCachedPhotoWithoutRequest(TestInlineQueryResultCachedPhotoBase): @@ -80,6 +82,10 @@ def test_expected_values(self, inline_query_result_cached_photo): assert ( inline_query_result_cached_photo.reply_markup.to_dict() == self.reply_markup.to_dict() ) + assert ( + inline_query_result_cached_photo.show_caption_above_media + == self.show_caption_above_media + ) def test_caption_entities_always_tuple(self): result = InlineQueryResultCachedPhoto(self.id_, self.photo_file_id) @@ -124,6 +130,10 @@ def test_to_dict(self, inline_query_result_cached_photo): inline_query_result_cached_photo_dict["reply_markup"] == inline_query_result_cached_photo.reply_markup.to_dict() ) + assert ( + inline_query_result_cached_photo_dict["show_caption_above_media"] + == inline_query_result_cached_photo.show_caption_above_media + ) def test_equality(self): a = InlineQueryResultCachedPhoto(self.id_, self.photo_file_id) diff --git a/tests/_inline/test_inlinequeryresultcachedvideo.py b/tests/_inline/test_inlinequeryresultcachedvideo.py index fd108551b5d..d520b3c7c10 100644 --- a/tests/_inline/test_inlinequeryresultcachedvideo.py +++ b/tests/_inline/test_inlinequeryresultcachedvideo.py @@ -41,6 +41,7 @@ def inline_query_result_cached_video(): description=TestInlineQueryResultCachedVideoBase.description, input_message_content=TestInlineQueryResultCachedVideoBase.input_message_content, reply_markup=TestInlineQueryResultCachedVideoBase.reply_markup, + show_caption_above_media=TestInlineQueryResultCachedVideoBase.show_caption_above_media, ) @@ -55,6 +56,7 @@ class TestInlineQueryResultCachedVideoBase: description = "description" input_message_content = InputTextMessageContent("input_message_content") reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) + show_caption_above_media = True class TestInlineQueryResultCachedVideoWithoutRequest(TestInlineQueryResultCachedVideoBase): @@ -80,6 +82,10 @@ def test_expected_values(self, inline_query_result_cached_video): assert ( inline_query_result_cached_video.reply_markup.to_dict() == self.reply_markup.to_dict() ) + assert ( + inline_query_result_cached_video.show_caption_above_media + == self.show_caption_above_media + ) def test_caption_entities_always_tuple(self): video = InlineQueryResultCachedVideo(self.id_, self.video_file_id, self.title) @@ -125,6 +131,10 @@ def test_to_dict(self, inline_query_result_cached_video): inline_query_result_cached_video_dict["reply_markup"] == inline_query_result_cached_video.reply_markup.to_dict() ) + assert ( + inline_query_result_cached_video_dict["show_caption_above_media"] + == inline_query_result_cached_video.show_caption_above_media + ) def test_equality(self): a = InlineQueryResultCachedVideo(self.id_, self.video_file_id, self.title) diff --git a/tests/_inline/test_inlinequeryresultgif.py b/tests/_inline/test_inlinequeryresultgif.py index abb3a936fd7..86ac8574a8e 100644 --- a/tests/_inline/test_inlinequeryresultgif.py +++ b/tests/_inline/test_inlinequeryresultgif.py @@ -45,6 +45,7 @@ def inline_query_result_gif(): 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, ) @@ -63,6 +64,7 @@ class TestInlineQueryResultGifBase: caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] input_message_content = InputTextMessageContent("input_message_content") reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) + show_caption_above_media = True class TestInlineQueryResultGifWithoutRequest(TestInlineQueryResultGifBase): @@ -94,6 +96,7 @@ def test_expected_values(self, inline_query_result_gif): == self.input_message_content.to_dict() ) assert inline_query_result_gif.reply_markup.to_dict() == self.reply_markup.to_dict() + assert inline_query_result_gif.show_caption_above_media == self.show_caption_above_media def test_to_dict(self, inline_query_result_gif): inline_query_result_gif_dict = inline_query_result_gif.to_dict() @@ -126,6 +129,10 @@ def test_to_dict(self, inline_query_result_gif): inline_query_result_gif_dict["reply_markup"] == inline_query_result_gif.reply_markup.to_dict() ) + assert ( + inline_query_result_gif_dict["show_caption_above_media"] + == inline_query_result_gif.show_caption_above_media + ) def test_equality(self): a = InlineQueryResultGif(self.id_, self.gif_url, self.thumbnail_url) diff --git a/tests/_inline/test_inlinequeryresultmpeg4gif.py b/tests/_inline/test_inlinequeryresultmpeg4gif.py index cd211bd4c2d..2a1bfc2cf93 100644 --- a/tests/_inline/test_inlinequeryresultmpeg4gif.py +++ b/tests/_inline/test_inlinequeryresultmpeg4gif.py @@ -45,6 +45,7 @@ def inline_query_result_mpeg4_gif(): 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, ) @@ -63,6 +64,7 @@ class TestInlineQueryResultMpeg4GifBase: caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] input_message_content = InputTextMessageContent("input_message_content") reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) + show_caption_above_media = True class TestInlineQueryResultMpeg4GifWithoutRequest(TestInlineQueryResultMpeg4GifBase): @@ -90,6 +92,9 @@ def test_expected_values(self, inline_query_result_mpeg4_gif): == self.input_message_content.to_dict() ) assert inline_query_result_mpeg4_gif.reply_markup.to_dict() == self.reply_markup.to_dict() + assert ( + inline_query_result_mpeg4_gif.show_caption_above_media == self.show_caption_above_media + ) def test_caption_entities_always_tuple(self): result = InlineQueryResultMpeg4Gif(self.id_, self.mpeg4_url, self.thumbnail_url) @@ -144,6 +149,10 @@ def test_to_dict(self, inline_query_result_mpeg4_gif): inline_query_result_mpeg4_gif_dict["reply_markup"] == inline_query_result_mpeg4_gif.reply_markup.to_dict() ) + assert ( + inline_query_result_mpeg4_gif_dict["show_caption_above_media"] + == inline_query_result_mpeg4_gif.show_caption_above_media + ) def test_equality(self): a = InlineQueryResultMpeg4Gif(self.id_, self.mpeg4_url, self.thumbnail_url) diff --git a/tests/_inline/test_inlinequeryresultphoto.py b/tests/_inline/test_inlinequeryresultphoto.py index 3078353ca97..323cf7b71ed 100644 --- a/tests/_inline/test_inlinequeryresultphoto.py +++ b/tests/_inline/test_inlinequeryresultphoto.py @@ -44,6 +44,7 @@ def inline_query_result_photo(): 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, ) @@ -62,6 +63,7 @@ class TestInlineQueryResultPhotoBase: input_message_content = InputTextMessageContent("input_message_content") reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) + show_caption_above_media = True class TestInlineQueryResultPhotoWithoutRequest(TestInlineQueryResultPhotoBase): @@ -88,6 +90,7 @@ def test_expected_values(self, inline_query_result_photo): == self.input_message_content.to_dict() ) assert inline_query_result_photo.reply_markup.to_dict() == self.reply_markup.to_dict() + assert inline_query_result_photo.show_caption_above_media == self.show_caption_above_media def test_caption_entities_always_tuple(self): result = InlineQueryResultPhoto(self.id_, self.photo_url, self.thumbnail_url) @@ -128,6 +131,10 @@ def test_to_dict(self, inline_query_result_photo): inline_query_result_photo_dict["reply_markup"] == inline_query_result_photo.reply_markup.to_dict() ) + assert ( + inline_query_result_photo_dict["show_caption_above_media"] + == inline_query_result_photo.show_caption_above_media + ) def test_equality(self): a = InlineQueryResultPhoto(self.id_, self.photo_url, self.thumbnail_url) diff --git a/tests/_inline/test_inlinequeryresultvideo.py b/tests/_inline/test_inlinequeryresultvideo.py index 40664af1992..68709e396e3 100644 --- a/tests/_inline/test_inlinequeryresultvideo.py +++ b/tests/_inline/test_inlinequeryresultvideo.py @@ -46,6 +46,7 @@ def inline_query_result_video(): description=TestInlineQueryResultVideoBase.description, input_message_content=TestInlineQueryResultVideoBase.input_message_content, reply_markup=TestInlineQueryResultVideoBase.reply_markup, + show_caption_above_media=TestInlineQueryResultVideoBase.show_caption_above_media, ) @@ -65,6 +66,7 @@ class TestInlineQueryResultVideoBase: description = "description" input_message_content = InputTextMessageContent("input_message_content") reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) + show_caption_above_media = True class TestInlineQueryResultVideoWithoutRequest(TestInlineQueryResultVideoBase): @@ -93,6 +95,7 @@ def test_expected_values(self, inline_query_result_video): == self.input_message_content.to_dict() ) assert inline_query_result_video.reply_markup.to_dict() == self.reply_markup.to_dict() + assert inline_query_result_video.show_caption_above_media == self.show_caption_above_media def test_caption_entities_always_tuple(self): video = InlineQueryResultVideo( @@ -140,6 +143,10 @@ def test_to_dict(self, inline_query_result_video): inline_query_result_video_dict["reply_markup"] == inline_query_result_video.reply_markup.to_dict() ) + assert ( + inline_query_result_video_dict["show_caption_above_media"] + == inline_query_result_video.show_caption_above_media + ) def test_equality(self): a = InlineQueryResultVideo( diff --git a/tests/_payment/test_invoice.py b/tests/_payment/test_invoice.py index 532fae0a89b..65a39aeb3f7 100644 --- a/tests/_payment/test_invoice.py +++ b/tests/_payment/test_invoice.py @@ -292,13 +292,14 @@ async def test_send_invoice_default_allow_sending_without_reply( self.title, self.description, self.payload, - provider_token, - self.currency, - self.prices, + "", # using tg stars + "XTR", + [self.prices[0]], allow_sending_without_reply=custom, reply_to_message_id=reply_to_message.message_id, ) assert message.reply_to_message is None + assert message.invoice.currency == "XTR" elif default_bot.defaults.allow_sending_without_reply: message = await default_bot.send_invoice( chat_id, diff --git a/tests/ext/test_filters.py b/tests/ext/test_filters.py index fc88e428404..97d17e2ebaf 100644 --- a/tests/ext/test_filters.py +++ b/tests/ext/test_filters.py @@ -1107,6 +1107,11 @@ def test_filters_game(self, update): update.message.game = "test" assert filters.GAME.check_update(update) + def test_filters_effect_id(self, update): + assert not filters.EFFECT_ID.check_update(update) + update.message.effect_id = "test" + assert filters.EFFECT_ID.check_update(update) + def test_entities_filter(self, update, message_entity): update.message.entities = [message_entity] assert filters.Entity(message_entity.type).check_update(update) diff --git a/tests/test_bot.py b/tests/test_bot.py index 047238907e6..8fa53628193 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -2180,6 +2180,16 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): monkeypatch.setattr(bot.request, "post", make_assertion) assert await bot.send_message(2, "text", business_connection_id=42) + async def test_message_effect_id_argument(self, bot, monkeypatch): + """We can't test every single method easily, so we just test one. Our linting will catch + any unused args with the others.""" + + async def make_assertion(url, request_data: RequestData, *args, **kwargs): + return request_data.parameters.get("message_effect_id") == 42 + + monkeypatch.setattr(bot.request, "post", make_assertion) + assert await bot.send_message(2, "text", message_effect_id=42) + async def test_get_business_connection(self, bot, monkeypatch): bci = "42" user = User(1, "first", False) @@ -2200,6 +2210,17 @@ async def do_request(*args, **kwargs): obj = await bot.get_business_connection(business_connection_id=bci) assert isinstance(obj, BusinessConnection) + async def test_refund_star_payment(self, bot, monkeypatch): + # can't make actual request so we just test that the correct data is passed + async def make_assertion(url, request_data: RequestData, *args, **kwargs): + return ( + request_data.parameters.get("user_id") == 42 + and request_data.parameters.get("telegram_payment_charge_id") == "37" + ) + + monkeypatch.setattr(bot.request, "post", make_assertion) + assert await bot.refund_star_payment(42, "37") + class TestBotWithRequest: """ @@ -2804,9 +2825,11 @@ async def test_edit_message_caption(self, bot, media_message): caption="new_caption", chat_id=media_message.chat_id, message_id=media_message.message_id, + show_caption_above_media=False, ) assert message.caption == "new_caption" + assert not message.show_caption_above_media async def test_edit_message_caption_entities(self, bot, media_message): test_string = "Italic Bold Code" @@ -3789,6 +3812,7 @@ async def test_copy_message_without_reply(self, bot, chat_id, media_message): parse_mode=ParseMode.HTML, reply_to_message_id=media_message.message_id, reply_markup=keyboard, + show_caption_above_media=False, ) # we send a temp message which replies to the returned message id in order to get a # message object diff --git a/tests/test_chat.py b/tests/test_chat.py index 36c1e80a800..a11b40c647b 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -711,8 +711,8 @@ async def make_assertion(*_, **kwargs): return from_chat_id and message_id and chat_id assert check_shortcut_signature(Chat.send_copy, Bot.copy_message, ["chat_id"], []) - assert await check_shortcut_call(chat.copy_message, chat.get_bot(), "copy_message") - assert await check_defaults_handling(chat.copy_message, chat.get_bot()) + assert await check_shortcut_call(chat.send_copy, chat.get_bot(), "copy_message") + assert await check_defaults_handling(chat.send_copy, chat.get_bot()) monkeypatch.setattr(chat.get_bot(), "copy_message", make_assertion) assert await chat.send_copy(from_chat_id="test_copy", message_id=42) diff --git a/tests/test_constants.py b/tests/test_constants.py index 98768b806b4..75368857325 100644 --- a/tests/test_constants.py +++ b/tests/test_constants.py @@ -173,10 +173,9 @@ def is_type_attribute(name: str) -> bool: "is_accessible", "quote", "external_reply", - # attribute is deprecated, no need to add it to MessageType - "user_shared", "via_bot", "is_from_offline", + "show_caption_above_media", } @pytest.mark.parametrize( diff --git a/tests/test_message.py b/tests/test_message.py index 46a7f89b865..c51e3a92a68 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -273,6 +273,8 @@ def message(bot): {"sender_business_bot": User(1, "BusinessBot", True)}, {"business_connection_id": "123456789"}, {"chat_background_set": ChatBackground(type=BackgroundTypeChatTheme("ice"))}, + {"effect_id": "123456789"}, + {"show_caption_above_media": True}, ], ids=[ "reply", @@ -342,6 +344,8 @@ def message(bot): "business_connection_id", "is_from_offline", "chat_background_set", + "effect_id", + "show_caption_above_media", ], ) def message_params(bot, request): @@ -399,11 +403,12 @@ class TestMessageBase: {"length": 2, "offset": 150, "type": "custom_emoji", "custom_emoji_id": "1"}, {"length": 34, "offset": 154, "type": "blockquote"}, {"length": 6, "offset": 181, "type": "bold"}, + {"length": 33, "offset": 190, "type": "expandable_blockquote"}, ] test_text_v2 = ( r"Test for trgh nested in italic. Python pre. Spoiled. " - "👍.\nMultiline\nblock quote\nwith nested." + "👍.\nMultiline\nblock quote\nwith nested.\n\nMultiline\nexpandable\nblock quote." ) test_message = Message( message_id=1, @@ -728,7 +733,8 @@ def test_text_html_simple(self): '
Python pre
. ' 'Spoiled. ' '👍.\n' - "
Multiline\nblock quote\nwith nested.
" + "
Multiline\nblock quote\nwith nested.
\n\n" + "
Multiline\nexpandable\nblock quote.
" ) text_html = self.test_message_v2.text_html assert text_html == test_html_string @@ -749,7 +755,8 @@ def test_text_html_urled(self): '
Python pre
. ' 'Spoiled. ' '👍.\n' - "
Multiline\nblock quote\nwith nested.
" + "
Multiline\nblock quote\nwith nested.
\n\n" + "
Multiline\nexpandable\nblock quote.
" ) text_html = self.test_message_v2.text_html_urled assert text_html == test_html_string @@ -774,6 +781,9 @@ def test_text_markdown_v2_simple(self): ">Multiline\n" ">block quote\n" r">with *nested*\." + "\n\n>Multiline\n" + ">expandable\n" + r">block quote\.||" ) text_markdown = self.test_message_v2.text_markdown_v2 assert text_markdown == test_md_string @@ -830,6 +840,9 @@ def test_text_markdown_v2_urled(self): ">Multiline\n" ">block quote\n" r">with *nested*\." + "\n\n>Multiline\n" + ">expandable\n" + r">block quote\.||" ) text_markdown = self.test_message_v2.text_markdown_v2_urled assert text_markdown == test_md_string @@ -946,7 +959,8 @@ def test_caption_html_simple(self): '
Python pre
. ' 'Spoiled. ' '👍.\n' - "
Multiline\nblock quote\nwith nested.
" + "
Multiline\nblock quote\nwith nested.
\n\n" + "
Multiline\nexpandable\nblock quote.
" ) caption_html = self.test_message_v2.caption_html assert caption_html == test_html_string @@ -967,7 +981,8 @@ def test_caption_html_urled(self): '
Python pre
. ' 'Spoiled. ' '👍.\n' - "
Multiline\nblock quote\nwith nested.
" + "
Multiline\nblock quote\nwith nested.
\n\n" + "
Multiline\nexpandable\nblock quote.
" ) caption_html = self.test_message_v2.caption_html_urled assert caption_html == test_html_string @@ -992,6 +1007,9 @@ def test_caption_markdown_v2_simple(self): ">Multiline\n" ">block quote\n" r">with *nested*\." + "\n\n>Multiline\n" + ">expandable\n" + r">block quote\.||" ) caption_markdown = self.test_message_v2.caption_markdown_v2 assert caption_markdown == test_md_string @@ -1023,6 +1041,9 @@ def test_caption_markdown_v2_urled(self): ">Multiline\n" ">block quote\n" r">with *nested*\." + "\n\n>Multiline\n" + ">expandable\n" + r">block quote\.||" ) caption_markdown = self.test_message_v2.caption_markdown_v2_urled assert caption_markdown == test_md_string @@ -1484,6 +1505,9 @@ async def test_reply_markdown_v2(self, monkeypatch, message): ">Multiline\n" ">block quote\n" r">with *nested*\." + "\n\n>Multiline\n" + ">expandable\n" + r">block quote\.||" ) async def make_assertion(*_, **kwargs): @@ -1534,7 +1558,8 @@ async def test_reply_html(self, monkeypatch, message): '
Python pre
. ' 'Spoiled. ' '👍.\n' - "
Multiline\nblock quote\nwith nested.
" + "
Multiline\nblock quote\nwith nested.
\n\n" + "
Multiline\nexpandable\nblock quote.
" ) async def make_assertion(*_, **kwargs): diff --git a/tests/test_official/exceptions.py b/tests/test_official/exceptions.py index 4b44d286b32..91f186ff738 100644 --- a/tests/test_official/exceptions.py +++ b/tests/test_official/exceptions.py @@ -172,7 +172,9 @@ def ignored_param_requirements(object_name: str) -> set[str]: # Arguments that are optional arguments for now for backwards compatibility -BACKWARDS_COMPAT_KWARGS: dict[str, set[str]] = {} +BACKWARDS_COMPAT_KWARGS: dict[str, set[str]] = { + "send_invoice|create_invoice_link|InputInvoiceMessageContent": {"provider_token"} +} def backwards_compat_kwargs(object_name: str) -> set[str]: diff --git a/tests/test_user.py b/tests/test_user.py index 86faa73cd77..06936532860 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -439,8 +439,8 @@ async def make_assertion(*_, **kwargs): return from_chat_id and message_id and user_id assert check_shortcut_signature(User.send_copy, Bot.copy_message, ["chat_id"], []) - assert await check_shortcut_call(user.copy_message, user.get_bot(), "copy_message") - assert await check_defaults_handling(user.copy_message, user.get_bot()) + assert await check_shortcut_call(user.send_copy, user.get_bot(), "copy_message") + assert await check_defaults_handling(user.send_copy, user.get_bot()) monkeypatch.setattr(user.get_bot(), "copy_message", make_assertion) assert await user.send_copy(from_chat_id="from_chat_id", message_id="message_id") @@ -700,3 +700,18 @@ async def make_assertion(*_, **kwargs): monkeypatch.setattr(user.get_bot(), "forward_messages", make_assertion) assert await user.forward_messages_to(chat_id="test_forwards", message_ids=(42, 43)) + + async def test_instance_method_refund_star_payment(self, monkeypatch, user): + async def make_assertion(*_, **kwargs): + return kwargs["user_id"] == user.id and kwargs["telegram_payment_charge_id"] == 42 + + assert check_shortcut_signature( + user.refund_star_payment, Bot.refund_star_payment, ["user_id"], [] + ) + assert await check_shortcut_call( + user.refund_star_payment, user.get_bot(), "refund_star_payment" + ) + assert await check_defaults_handling(user.refund_star_payment, user.get_bot()) + + monkeypatch.setattr(user.get_bot(), "refund_star_payment", make_assertion) + assert await user.refund_star_payment(telegram_payment_charge_id=42) From 9e70ac8b7aad9848cf6a620d7489413e7621c951 Mon Sep 17 00:00:00 2001 From: Trijeet Modak Date: Thu, 6 Jun 2024 21:01:45 +0530 Subject: [PATCH 7/9] Add Parameter `chat_id` to `ChatMemberHandler` (#4290) --- telegram/ext/_handlers/chatmemberhandler.py | 37 +++++++++---- tests/ext/test_chatmemberhandler.py | 60 +++++++++++++++++++++ 2 files changed, 86 insertions(+), 11 deletions(-) diff --git a/telegram/ext/_handlers/chatmemberhandler.py b/telegram/ext/_handlers/chatmemberhandler.py index 87232d84261..6d0add00c45 100644 --- a/telegram/ext/_handlers/chatmemberhandler.py +++ b/telegram/ext/_handlers/chatmemberhandler.py @@ -21,8 +21,9 @@ from telegram import Update from telegram._utils.defaultvalue import DEFAULT_TRUE -from telegram._utils.types import DVType +from telegram._utils.types import SCT, DVType from telegram.ext._handlers.basehandler import BaseHandler +from telegram.ext._utils._update_parsing import parse_chat_id from telegram.ext._utils.types import CCT, HandlerCallback RT = TypeVar("RT") @@ -58,6 +59,9 @@ async def callback(update: Update, context: CallbackContext) :meth:`telegram.ext.Application.process_update`. Defaults to :obj:`True`. .. seealso:: :wiki:`Concurrency` + chat_id (:obj:`int` | Collection[:obj:`int`], optional): Filters chat member updates from + specified chat ID(s) only. + .. versionadded:: NEXT.VERSION Attributes: callback (:term:`coroutine function`): The callback function for this handler. @@ -70,7 +74,10 @@ async def callback(update: Update, context: CallbackContext) """ - __slots__ = ("chat_member_types",) + __slots__ = ( + "_chat_ids", + "chat_member_types", + ) MY_CHAT_MEMBER: Final[int] = -1 """:obj:`int`: Used as a constant to handle only :attr:`telegram.Update.my_chat_member`.""" CHAT_MEMBER: Final[int] = 0 @@ -84,10 +91,12 @@ def __init__( callback: HandlerCallback[Update, CCT, RT], chat_member_types: int = MY_CHAT_MEMBER, block: DVType[bool] = DEFAULT_TRUE, + chat_id: Optional[SCT[int]] = None, ): super().__init__(callback, block=block) self.chat_member_types: Optional[int] = chat_member_types + self._chat_ids = parse_chat_id(chat_id) def check_update(self, update: object) -> bool: """Determines whether an update should be passed to this handler's :attr:`callback`. @@ -99,12 +108,18 @@ def check_update(self, update: object) -> bool: :obj:`bool` """ - if isinstance(update, Update): - if not (update.my_chat_member or update.chat_member): - return False - if self.chat_member_types == self.ANY_CHAT_MEMBER: - return True - if self.chat_member_types == self.CHAT_MEMBER: - return bool(update.chat_member) - return bool(update.my_chat_member) - return False + if not isinstance(update, Update): + return False + if not (update.my_chat_member or update.chat_member): + return False + if ( + self._chat_ids + and update.effective_chat + and update.effective_chat.id not in self._chat_ids + ): + return False + if self.chat_member_types == self.ANY_CHAT_MEMBER: + return True + if self.chat_member_types == self.CHAT_MEMBER: + return bool(update.chat_member) + return bool(update.my_chat_member) diff --git a/tests/ext/test_chatmemberhandler.py b/tests/ext/test_chatmemberhandler.py index 9182b2a1124..ada95288cfc 100644 --- a/tests/ext/test_chatmemberhandler.py +++ b/tests/ext/test_chatmemberhandler.py @@ -144,6 +144,66 @@ async def test_chat_member_types( await app.process_update(chat_member) assert self.test_flag == result_2 + @pytest.mark.parametrize( + argnames=["allowed_types", "chat_id", "expected"], + argvalues=[ + (ChatMemberHandler.MY_CHAT_MEMBER, None, (True, False)), + (ChatMemberHandler.CHAT_MEMBER, None, (False, True)), + (ChatMemberHandler.ANY_CHAT_MEMBER, None, (True, True)), + (ChatMemberHandler.MY_CHAT_MEMBER, 1, (True, False)), + (ChatMemberHandler.CHAT_MEMBER, 1, (False, True)), + (ChatMemberHandler.ANY_CHAT_MEMBER, 1, (True, True)), + (ChatMemberHandler.MY_CHAT_MEMBER, [1], (True, False)), + (ChatMemberHandler.CHAT_MEMBER, [1], (False, True)), + (ChatMemberHandler.ANY_CHAT_MEMBER, [1], (True, True)), + (ChatMemberHandler.MY_CHAT_MEMBER, 2, (False, False)), + (ChatMemberHandler.CHAT_MEMBER, 2, (False, False)), + (ChatMemberHandler.ANY_CHAT_MEMBER, 2, (False, False)), + (ChatMemberHandler.MY_CHAT_MEMBER, [2], (False, False)), + (ChatMemberHandler.CHAT_MEMBER, [2], (False, False)), + (ChatMemberHandler.ANY_CHAT_MEMBER, [2], (False, False)), + ], + ids=[ + "MY_CHAT_MEMBER", + "CHAT_MEMBER", + "ANY_CHAT_MEMBER", + "MY_CHAT_MEMBER, CHAT=1 ", + "CHAT_MEMBER, CHAT=1", + "ANY_CHAT_MEMBER, CHAT=1", + "MY_CHAT_MEMBER, CHAT=[1] ", + "CHAT_MEMBER, CHAT=[1]", + "ANY_CHAT_MEMBER, CHAT=[1]", + "MY_CHAT_MEMBER, CHAT=2 ", + "CHAT_MEMBER, CHAT=2", + "ANY_CHAT_MEMBER, CHAT=2", + "MY_CHAT_MEMBER, CHAT=[2] ", + "CHAT_MEMBER, CHAT=[2]", + "ANY_CHAT_MEMBER, CHAT=[2]", + ], + ) + async def test_chat_member_types_with_chat_id( + self, app, chat_member_updated, chat_member, expected, allowed_types, chat_id + ): + result_1, result_2 = expected + + handler = ChatMemberHandler( + self.callback, chat_member_types=allowed_types, chat_id=chat_id + ) + app.add_handler(handler) + + async with app: + assert handler.check_update(chat_member) == result_1 + await app.process_update(chat_member) + assert self.test_flag == result_1 + + self.test_flag = False + chat_member.my_chat_member = None + chat_member.chat_member = chat_member_updated + + assert handler.check_update(chat_member) == result_2 + await app.process_update(chat_member) + assert self.test_flag == result_2 + def test_other_update_types(self, false_update): handler = ChatMemberHandler(self.callback) assert not handler.check_update(false_update) From 78c945d4857b12b1f19df10d86686e2de25821b6 Mon Sep 17 00:00:00 2001 From: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:13:55 +0200 Subject: [PATCH 8/9] Documentation Improvements (#4264) --- docs/source/conf.py | 8 ++++++-- telegram/_bot.py | 7 ++++--- telegram/_message.py | 12 ++++++------ 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index a50e3dbdb05..5858a79e2da 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -20,9 +20,13 @@ # built documents. # # The short X.Y version. -version = "21.2" # telegram.__version__[:3] + +# Import needs to be below the sys.path.insert above +import telegram # noqa: E402 + +version = telegram.__version__ # The full version, including alpha/beta/rc tags. -release = "21.2" # telegram.__version__ +release = telegram.__version__ # If your documentation needs a minimal Sphinx version, state it here. needs_sphinx = "6.1.3" diff --git a/telegram/_bot.py b/telegram/_bot.py index 94e70fe6c3d..93b7f533021 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -7536,7 +7536,7 @@ async def log_out( minutes. Returns: - :obj:`True`: On success + :obj:`True`: On success, :obj:`True` is returned. Raises: :class:`telegram.error.TelegramError` @@ -7567,7 +7567,7 @@ async def close( 10 minutes after the bot is launched. Returns: - :obj:`True`: On success + :obj:`True`: On success, :obj:`True` is returned. Raises: :class:`telegram.error.TelegramError` @@ -7664,7 +7664,8 @@ async def copy_message( |keyword_only_arg| Returns: - :class:`telegram.MessageId`: On success + :class:`telegram.MessageId`: On success, the :class:`telegram.MessageId` of the sent + message is returned. Raises: :class:`telegram.error.TelegramError` diff --git a/telegram/_message.py b/telegram/_message.py index 43b7519b5bf..4c74ad4404b 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -255,8 +255,8 @@ class Message(MaybeInaccessibleMessage): .. versionchanged:: 20.8 * This class is now a subclass of :class:`telegram.MaybeInaccessibleMessage`. - * The :paramref:`pinned_message` now can be either class:`telegram.Message` or - class:`telegram.InaccessibleMessage`. + * The :paramref:`pinned_message` now can be either :class:`telegram.Message` or + :class:`telegram.InaccessibleMessage`. .. versionchanged:: 20.0 @@ -419,8 +419,8 @@ class Message(MaybeInaccessibleMessage): :attr:`reply_to_message` fields even if it is itself a reply. .. versionchanged:: 20.8 - This attribute now is either class:`telegram.Message` or - class:`telegram.InaccessibleMessage`. + This attribute now is either :class:`telegram.Message` or + :class:`telegram.InaccessibleMessage`. invoice (:class:`telegram.Invoice`, optional): Message is an invoice for a payment, information about the invoice. successful_payment (:class:`telegram.SuccessfulPayment`, optional): Message is a service @@ -731,8 +731,8 @@ class Message(MaybeInaccessibleMessage): :attr:`reply_to_message` fields even if it is itself a reply. .. versionchanged:: 20.8 - This attribute now is either class:`telegram.Message` or - class:`telegram.InaccessibleMessage`. + This attribute now is either :class:`telegram.Message` or + :class:`telegram.InaccessibleMessage`. invoice (:class:`telegram.Invoice`): Optional. Message is an invoice for a payment, information about the invoice. successful_payment (:class:`telegram.SuccessfulPayment`): Optional. Message is a service From a9f6afd0152e30f32db69d35211fc05eceb9edfe Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:52:22 +0200 Subject: [PATCH 9/9] Bump version to v21.3 --- CHANGES.rst | 34 +++++++++++++ README_RAW.rst | 2 +- telegram/__init__.py | 2 +- telegram/_bot.py | 50 +++++++++---------- telegram/_chat.py | 4 +- telegram/_chatfullinfo.py | 2 +- telegram/_files/inputmedia.py | 12 ++--- .../_inline/inlinequeryresultcachedgif.py | 4 +- .../inlinequeryresultcachedmpeg4gif.py | 4 +- .../_inline/inlinequeryresultcachedphoto.py | 4 +- .../_inline/inlinequeryresultcachedvideo.py | 4 +- telegram/_inline/inlinequeryresultgif.py | 4 +- telegram/_inline/inlinequeryresultmpeg4gif.py | 4 +- telegram/_inline/inlinequeryresultphoto.py | 4 +- telegram/_inline/inlinequeryresultvideo.py | 4 +- .../_inline/inputinvoicemessagecontent.py | 2 +- telegram/_message.py | 8 +-- telegram/_messageentity.py | 2 +- telegram/_user.py | 2 +- telegram/_version.py | 2 +- telegram/constants.py | 4 +- telegram/ext/_handlers/chatmemberhandler.py | 2 +- telegram/ext/filters.py | 2 +- 23 files changed, 98 insertions(+), 64 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9a509ab485c..79a6f124496 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,40 @@ Changelog ========= +Version 21.3 +============ +*Released 2024-06-07* + +This is the technical changelog for version 21.3. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel `_. + +Major Changes +------------- + +- Full Support for Bot API 7.4 (:pr:`4286`, :pr:`4276` closes :issue:`4275`, :pr:`4285`, :pr:`4283`, :pr:`4280`, :pr:`4278`, :pr:`4279`) +- Deprecate ``python-telegram-bot-raw`` (:pr:`4270`) +- Remove Functionality Deprecated in Bot API 7.3 (:pr:`4266` closes :issue:`4244`) + +New Features +------------ + +- Add Parameter ``chat_id`` to ``ChatMemberHandler`` (:pr:`4290` by `uniquetrij `_ closes :issue:`4287`) + +Documentation Improvements +-------------------------- + +- Documentation Improvements (:pr:`4264` closes :issue:`4240`) + +Internal Changes +---------------- + +- Add ``setuptools`` to ``requirements-dev.txt`` (:pr:`4282`) +- Update Settings for pre-commit.ci (:pr:`4265`) + +Dependency Updates +------------------ + +- Bump ``pytest`` from 8.2.0 to 8.2.1 (:pr:`4272`) + Version 21.2 ============ diff --git a/README_RAW.rst b/README_RAW.rst index 7278f47aeeb..e82270959f1 100644 --- a/README_RAW.rst +++ b/README_RAW.rst @@ -62,7 +62,7 @@ ⚠️ Deprecation Notice ===================== -The ``python-telegram-bot-raw`` library will no longer be updated after NEXT.VERSION. +The ``python-telegram-bot-raw`` library will no longer be updated after 21.3. Please instead use the ``python-telegram-bot`` `library `_. The change requires no changes in your code and requires no additional dependencies. For additional information, please see this `channel post `_. diff --git a/telegram/__init__.py b/telegram/__init__.py index 1230716e785..6105c9780c8 100644 --- a/telegram/__init__.py +++ b/telegram/__init__.py @@ -494,7 +494,7 @@ # seeing the warning. warn( - warnings.PTBDeprecationWarning(version="NEXT.VERSION", message=_MESSAGE), + warnings.PTBDeprecationWarning(version="21.3", message=_MESSAGE), stacklevel=2, ) warn( diff --git a/telegram/_bot.py b/telegram/_bot.py index 93b7f533021..ebc7817b9d2 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -976,7 +976,7 @@ async def send_message( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -1350,10 +1350,10 @@ async def send_photo( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -1508,7 +1508,7 @@ async def send_audio( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -1662,7 +1662,7 @@ async def send_document( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -1788,7 +1788,7 @@ async def send_sticker( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -1944,10 +1944,10 @@ async def send_video( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -2096,7 +2096,7 @@ async def send_video_note( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -2253,10 +2253,10 @@ async def send_animation( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -2408,7 +2408,7 @@ async def send_voice( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -2534,7 +2534,7 @@ async def send_media_group( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -2717,7 +2717,7 @@ async def send_location( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -3014,7 +3014,7 @@ async def send_venue( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -3156,7 +3156,7 @@ async def send_contact( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -3276,7 +3276,7 @@ async def send_game( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -4079,7 +4079,7 @@ async def edit_message_caption( inline keyboard. show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Returns: :class:`telegram.Message`: On success, if edited message is not an inline message, the @@ -4976,7 +4976,7 @@ async def send_invoice( `@BotFather `_. Pass an empty string for payments in |tg_stars|. - .. deprecated:: NEXT.VERSION + .. deprecated:: 21.3 As of Bot API 7.4, this parameter is now optional and future versions of the library will make it optional as well. @@ -5057,7 +5057,7 @@ async def send_invoice( .. versionadded:: 20.8 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -7045,7 +7045,7 @@ async def send_poll( .. versionadded:: 21.2 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -7218,7 +7218,7 @@ async def send_dice( .. versionadded:: 21.1 message_effect_id (:obj:`str`, optional): |message_effect_id| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -7641,7 +7641,7 @@ async def copy_message( .. versionadded:: 20.8 show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Keyword Args: allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| @@ -7910,7 +7910,7 @@ async def create_invoice_link( `@BotFather `_. Pass an empty string for payments in |tg_stars|. - .. deprecated:: NEXT.VERSION + .. deprecated:: 21.3 As of Bot API 7.4, this parameter is now optional and future versions of the library will make it optional as well. @@ -9042,7 +9042,7 @@ async def refund_star_payment( ) -> bool: """Refunds a successful payment in |tg_stars|. - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Args: user_id (:obj:`int`): User identifier of the user whose payment will be refunded. diff --git a/telegram/_chat.py b/telegram/_chat.py index 39acf55aacc..b5e2d111f1a 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -69,7 +69,7 @@ class _ChatBase(TelegramObject): """Base class for :class:`telegram.Chat` and :class:`telegram.ChatFullInfo`. - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 """ __slots__ = ("first_name", "id", "is_forum", "last_name", "title", "type", "username") @@ -3277,7 +3277,7 @@ class Chat(_ChatBase): this field for backwards compatibility, it is available through :attr:`~telegram.TelegramObject.api_kwargs`. - .. versionchanged:: NEXT.VERSION + .. versionchanged:: 21.3 As per Bot API 7.3, most of the arguments and attributes of this class have now moved to :class:`telegram.ChatFullInfo`. diff --git a/telegram/_chatfullinfo.py b/telegram/_chatfullinfo.py index 221b8f623cc..213baed7ef2 100644 --- a/telegram/_chatfullinfo.py +++ b/telegram/_chatfullinfo.py @@ -44,7 +44,7 @@ class ChatFullInfo(_ChatBase): .. versionadded:: 21.2 - .. versionchanged:: NEXT.VERSION + .. versionchanged:: 21.3 Explicit support for all shortcut methods known from :class:`telegram.Chat` on this object. Previously those were only available because this class inherited from :class:`telegram.Chat`. diff --git a/telegram/_files/inputmedia.py b/telegram/_files/inputmedia.py index 229e7320907..0cf5955a4d3 100644 --- a/telegram/_files/inputmedia.py +++ b/telegram/_files/inputmedia.py @@ -162,7 +162,7 @@ class InputMediaAnimation(InputMedia): .. versionadded:: 20.2 show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.ANIMATION`. @@ -189,7 +189,7 @@ class InputMediaAnimation(InputMedia): .. versionadded:: 20.2 show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 """ __slots__ = ( @@ -277,7 +277,7 @@ class InputMediaPhoto(InputMedia): .. versionadded:: 20.0 show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.PHOTO`. @@ -298,7 +298,7 @@ class InputMediaPhoto(InputMedia): .. versionadded:: 20.0 show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 """ __slots__ = ( @@ -387,7 +387,7 @@ class InputMediaVideo(InputMedia): .. versionadded:: 20.2 show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.VIDEO`. @@ -416,7 +416,7 @@ class InputMediaVideo(InputMedia): .. versionadded:: 20.2 show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 """ __slots__ = ( diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index 9516accae04..9f52347a05c 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -61,7 +61,7 @@ class InlineQueryResultCachedGif(InlineQueryResult): message to be sent instead of the gif. show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.GIF`. @@ -86,7 +86,7 @@ class InlineQueryResultCachedGif(InlineQueryResult): message to be sent instead of the gif. show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 """ diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index 3fa2a8f1393..f750f4df8fd 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -61,7 +61,7 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): message to be sent instead of the MPEG-4 file. show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.MPEG4GIF`. @@ -86,7 +86,7 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): message to be sent instead of the MPEG-4 file. show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 """ diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index 6c18630fff2..75f292d2e32 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -62,7 +62,7 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): message to be sent instead of the photo. show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.PHOTO`. @@ -88,7 +88,7 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): message to be sent instead of the photo. show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 """ diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index 7ce8c423faa..99a58eebbe5 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -58,7 +58,7 @@ class InlineQueryResultCachedVideo(InlineQueryResult): message to be sent instead of the video. show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VIDEO`. @@ -84,7 +84,7 @@ class InlineQueryResultCachedVideo(InlineQueryResult): message to be sent instead of the video. show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 """ diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index ab68f083daa..13e1f253b99 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -80,7 +80,7 @@ class InlineQueryResultGif(InlineQueryResult): message to be sent instead of the GIF animation. show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Raises: :class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is @@ -120,7 +120,7 @@ class InlineQueryResultGif(InlineQueryResult): message to be sent instead of the GIF animation. show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 """ diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index 6478340f745..1fff848418d 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -82,7 +82,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): message to be sent instead of the video animation. show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. 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. @@ -122,7 +122,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): message to be sent instead of the video animation. show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 """ __slots__ = ( diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index 1fbb4f4d649..637e952d4d0 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -76,7 +76,7 @@ class InlineQueryResultPhoto(InlineQueryResult): message to be sent instead of the photo. show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Raises: :class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is @@ -110,7 +110,7 @@ class InlineQueryResultPhoto(InlineQueryResult): message to be sent instead of the photo. show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 """ diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index 2c387b17c46..90bf4c86d3d 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -90,7 +90,7 @@ class InlineQueryResultVideo(InlineQueryResult): (e.g., a YouTube video). show_caption_above_media (:obj:`bool`, optional): Pass |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Raises: :class:`ValueError`: If neither :paramref:`thumbnail_url` nor :paramref:`thumb_url` is @@ -132,7 +132,7 @@ class InlineQueryResultVideo(InlineQueryResult): (e.g., a YouTube video). show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 """ diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index d710085fdab..74ea97de297 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -52,7 +52,7 @@ class InputInvoiceMessageContent(InputMessageContent): `@Botfather `_. Pass an empty string for payments in |tg_stars|. - .. deprecated:: NEXT.VERSION + .. deprecated:: 21.3 As of Bot API 7.4, this parameter is now optional and future versions of the library will make it optional as well. currency (:obj:`str`): Three-letter ISO 4217 currency code, see more on diff --git a/telegram/_message.py b/telegram/_message.py index 4c74ad4404b..b0605cd094d 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -331,7 +331,7 @@ class Message(MaybeInaccessibleMessage): effect_id (:obj:`str`, optional): Unique identifier of the message effect added to the message. - ..versionadded:: NEXT.VERSION + ..versionadded:: 21.3 caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): For messages with a Caption. Special entities like usernames, URLs, bot commands, etc. that appear in the @@ -344,7 +344,7 @@ class Message(MaybeInaccessibleMessage): show_caption_above_media (:obj:`bool`, optional): |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 audio (:class:`telegram.Audio`, optional): Message is an audio file, information about the file. document (:class:`telegram.Document`, optional): Message is a general file, information @@ -628,7 +628,7 @@ class Message(MaybeInaccessibleMessage): effect_id (:obj:`str`): Optional. Unique identifier of the message effect added to the message. - ..versionadded:: NEXT.VERSION + ..versionadded:: 21.3 caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. For messages with a Caption. Special entities like usernames, URLs, bot commands, etc. that appear in the @@ -641,7 +641,7 @@ class Message(MaybeInaccessibleMessage): show_caption_above_media (:obj:`bool`): Optional. |show_cap_above_med| - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 audio (:class:`telegram.Audio`): Optional. Message is an audio file, information about the file. diff --git a/telegram/_messageentity.py b/telegram/_messageentity.py index ebdcb8e077c..2f7fb7d6179 100644 --- a/telegram/_messageentity.py +++ b/telegram/_messageentity.py @@ -165,7 +165,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MessageEntit EXPANDABLE_BLOCKQUOTE: Final[str] = constants.MessageEntityType.EXPANDABLE_BLOCKQUOTE """:const:`telegram.constants.MessageEntityType.EXPANDABLE_BLOCKQUOTE` - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 """ HASHTAG: Final[str] = constants.MessageEntityType.HASHTAG """:const:`telegram.constants.MessageEntityType.HASHTAG`""" diff --git a/telegram/_user.py b/telegram/_user.py index 84ab7728cee..7ea769f28b6 100644 --- a/telegram/_user.py +++ b/telegram/_user.py @@ -2163,7 +2163,7 @@ async def refund_star_payment( For the documentation of the arguments, please see :meth:`telegram.Bot.refund_star_payment`. - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Returns: :obj:`bool`: On success, :obj:`True` is returned. diff --git a/telegram/_version.py b/telegram/_version.py index e1a1bbe79f2..34849536283 100644 --- a/telegram/_version.py +++ b/telegram/_version.py @@ -51,7 +51,7 @@ def __str__(self) -> str: __version_info__: Final[Version] = Version( - major=21, minor=2, micro=0, releaselevel="final", serial=0 + major=21, minor=3, micro=0, releaselevel="final", serial=0 ) __version__: Final[str] = str(__version_info__) diff --git a/telegram/constants.py b/telegram/constants.py index faf797231a8..5e2c853baa7 100644 --- a/telegram/constants.py +++ b/telegram/constants.py @@ -1653,7 +1653,7 @@ class MessageEntityType(StringEnum): EXPANDABLE_BLOCKQUOTE = "expandable_blockquote" """:obj:`str`: Message entities representing collapsed-by-default block quotation. - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 """ HASHTAG = "hashtag" """:obj:`str`: Message entities representing a hashtag.""" @@ -1809,7 +1809,7 @@ class MessageType(StringEnum): EFFECT_ID = "effect_id" """:obj:`str`: Messages with :attr:`telegram.Message.effect_id`. - .. versionadded:: NEXT.VERSION""" + .. versionadded:: 21.3""" FORUM_TOPIC_CREATED = "forum_topic_created" """:obj:`str`: Messages with :attr:`telegram.Message.forum_topic_created`. diff --git a/telegram/ext/_handlers/chatmemberhandler.py b/telegram/ext/_handlers/chatmemberhandler.py index 6d0add00c45..592361b64c5 100644 --- a/telegram/ext/_handlers/chatmemberhandler.py +++ b/telegram/ext/_handlers/chatmemberhandler.py @@ -61,7 +61,7 @@ async def callback(update: Update, context: CallbackContext) .. seealso:: :wiki:`Concurrency` chat_id (:obj:`int` | Collection[:obj:`int`], optional): Filters chat member updates from specified chat ID(s) only. - .. versionadded:: NEXT.VERSION + .. versionadded:: 21.3 Attributes: callback (:term:`coroutine function`): The callback function for this handler. diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index 24dc982e653..5147574e07a 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -1349,7 +1349,7 @@ def filter(self, message: Message) -> bool: EFFECT_ID = _EffectId(name="filters.EFFECT_ID") """Messages that contain :attr:`telegram.Message.effect_id`. -.. versionadded:: NEXT.VERSION""" +.. versionadded:: 21.3""" class Entity(MessageFilter):