diff --git a/README.rst b/README.rst index 1d6b20aafea..3344d171211 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,7 @@ :target: https://pypi.org/project/python-telegram-bot/ :alt: Supported Python versions -.. image:: https://img.shields.io/badge/Bot%20API-7.2-blue?logo=telegram +.. image:: https://img.shields.io/badge/Bot%20API-7.3-blue?logo=telegram :target: https://core.telegram.org/bots/api-changelog :alt: Supported Bot API versions @@ -89,7 +89,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.2** are supported. +All types and methods of the Telegram Bot API **7.3** are supported. Installing ========== diff --git a/README_RAW.rst b/README_RAW.rst index df1312e4857..7539408e7c8 100644 --- a/README_RAW.rst +++ b/README_RAW.rst @@ -14,7 +14,7 @@ :target: https://pypi.org/project/python-telegram-bot-raw/ :alt: Supported Python versions -.. image:: https://img.shields.io/badge/Bot%20API-7.2-blue?logo=telegram +.. image:: https://img.shields.io/badge/Bot%20API-7.3-blue?logo=telegram :target: https://core.telegram.org/bots/api-changelog :alt: Supported Bot API versions @@ -85,7 +85,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.2** are supported. +All types and methods of the Telegram Bot API **7.3** are supported. Installing ========== diff --git a/docs/source/telegram.at-tree.rst b/docs/source/telegram.at-tree.rst index 3d78292588a..7e240de0041 100644 --- a/docs/source/telegram.at-tree.rst +++ b/docs/source/telegram.at-tree.rst @@ -36,6 +36,7 @@ Available Types telegram.chatboostsourcegiveaway telegram.chatboostsourcepremium telegram.chatboostupdated + telegram.chatfullinfo telegram.chatinvitelink telegram.chatjoinrequest telegram.chatlocation diff --git a/docs/source/telegram.chatfullinfo.rst b/docs/source/telegram.chatfullinfo.rst new file mode 100644 index 00000000000..f15dbeedaa1 --- /dev/null +++ b/docs/source/telegram.chatfullinfo.rst @@ -0,0 +1,6 @@ +ChatFullInfo +============ + +.. autoclass:: telegram.ChatFullInfo + :members: + :show-inheritance: \ No newline at end of file diff --git a/telegram/__init__.py b/telegram/__init__.py index 304425c4d61..5054cd8863c 100644 --- a/telegram/__init__.py +++ b/telegram/__init__.py @@ -54,6 +54,7 @@ "ChatBoostSourceGiveaway", "ChatBoostSourcePremium", "ChatBoostUpdated", + "ChatFullInfo", "ChatInviteLink", "ChatJoinRequest", "ChatLocation", @@ -269,6 +270,7 @@ ChatBoostUpdated, UserChatBoosts, ) +from ._chatfullinfo import ChatFullInfo from ._chatinvitelink import ChatInviteLink from ._chatjoinrequest import ChatJoinRequest from ._chatlocation import ChatLocation diff --git a/telegram/_bot.py b/telegram/_bot.py index 8bb4af23de7..41849cbd029 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -58,9 +58,9 @@ from telegram._botdescription import BotDescription, BotShortDescription from telegram._botname import BotName from telegram._business import BusinessConnection -from telegram._chat import Chat from telegram._chatadministratorrights import ChatAdministratorRights from telegram._chatboost import UserChatBoosts +from telegram._chatfullinfo import ChatFullInfo from telegram._chatinvitelink import ChatInviteLink from telegram._chatmember import ChatMember from telegram._chatpermissions import ChatPermissions @@ -4434,16 +4434,19 @@ async def get_chat( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Chat: + ) -> ChatFullInfo: """ Use this method to get up to date information about the chat (current name of the user for one-on-one conversations, current username of a user, group or channel, etc.). + .. versionchanged:: NEXT.VERSION + In accordance to Bot API 7.3, this method now returns a :class:`telegram.ChatFullInfo`. + Args: chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| Returns: - :class:`telegram.Chat` + :class:`telegram.ChatFullInfo` Raises: :class:`telegram.error.TelegramError` @@ -4461,7 +4464,7 @@ async def get_chat( api_kwargs=api_kwargs, ) - return Chat.de_json(result, self) # type: ignore[return-value] + return ChatFullInfo.de_json(result, self) # type: ignore[return-value] async def get_chat_administrators( self, diff --git a/telegram/_chat.py b/telegram/_chat.py index 94991c9b391..9723e7edf9f 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -20,7 +20,7 @@ """This module contains an object that represents a Telegram Chat.""" from datetime import datetime from html import escape -from typing import TYPE_CHECKING, Final, Optional, Sequence, Tuple, Union +from typing import TYPE_CHECKING, Any, Final, Optional, Sequence, Tuple, Union from telegram import constants from telegram._birthdate import Birthdate @@ -36,9 +36,11 @@ 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 ( @@ -74,6 +76,45 @@ ) +_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. @@ -107,62 +148,134 @@ class Chat(TelegramObject): 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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 need to be approved by supergroup administrators. Returned only in :meth:`telegram.Bot.get_chat`. .. versionadded:: 20.0 + + .. deprecated:: NEXT.VERSION + 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:: NEXT.VERSION + 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). @@ -173,27 +286,47 @@ class Chat(TelegramObject): only in :meth:`telegram.Bot.get_chat`. .. versionadded:: 20.0 + + .. deprecated:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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`_ @@ -201,62 +334,110 @@ class Chat(TelegramObject): :meth:`telegram.Bot.get_chat`. .. versionadded:: 20.8 + + .. deprecated:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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. @@ -271,62 +452,134 @@ class Chat(TelegramObject): 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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 need to be approved by supergroup administrators. Returned only in :meth:`telegram.Bot.get_chat`. .. versionadded:: 20.0 + + .. deprecated:: NEXT.VERSION + 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:: NEXT.VERSION + 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). @@ -339,27 +592,47 @@ class Chat(TelegramObject): obtained via :meth:`~telegram.Bot.get_chat`. .. versionadded:: 20.0 + + .. deprecated:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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`_ @@ -367,62 +640,110 @@ class Chat(TelegramObject): :meth:`telegram.Bot.get_chat`. .. versionadded:: 20.8 + + .. deprecated:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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:: NEXT.VERSION + 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 """ @@ -471,7 +792,6 @@ class Chat(TelegramObject): "unrestrict_boost_count", "username", ) - SENDER: Final[str] = constants.ChatType.SENDER """:const:`telegram.constants.ChatType.SENDER` @@ -518,7 +838,7 @@ def __init__( 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, + 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, @@ -585,10 +905,30 @@ def __init__( 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( + f"The argument `{arg}` is deprecated and will only be available via " + "`ChatFullInfo` in the future.", + stacklevel=2, + category=PTBDeprecationWarning, + ) + self._id_attrs = (self.id,) self._freeze() + def __getattribute__(self, name: str) -> Any: + if name in _deprecated_attrs and self.__class__ is Chat: + warn( + f"The attribute `{name}` is deprecated and will only be accessible via " + "`ChatFullInfo` in the future.", + stacklevel=2, + category=PTBDeprecationWarning, + ) + return super().__getattribute__(name) + @property def effective_name(self) -> Optional[str]: """ @@ -658,7 +998,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Chat"]: 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"] = cls.de_json(data.get("personal_chat"), 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( diff --git a/telegram/_chatfullinfo.py b/telegram/_chatfullinfo.py new file mode 100644 index 00000000000..7b7c686b086 --- /dev/null +++ b/telegram/_chatfullinfo.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python +# pylint: disable=redefined-builtin +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram ChatFullInfo.""" +from datetime import datetime +from typing import TYPE_CHECKING, Optional, Sequence + +from telegram._birthdate import Birthdate +from telegram._chat import Chat +from telegram._chatlocation import ChatLocation +from telegram._chatpermissions import ChatPermissions +from telegram._files.chatphoto import ChatPhoto +from telegram._reaction import ReactionType +from telegram._utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import BusinessIntro, BusinessLocation, BusinessOpeningHours, Message + + +class ChatFullInfo(Chat): + """ + This object contains full information about a chat. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`~telegram.Chat.id` is equal. + + .. versionadded:: NEXT.VERSION + + 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`. + + Args: + max_reaction_count (:obj:`int`): The maximum number of reactions that can be set on a + message in the chat. + + .. versionadded:: NEXT.VERSION + + Attributes: + max_reaction_count (:obj:`int`): The maximum number of reactions that can be set on a + message in the chat. + + .. versionadded:: NEXT.VERSION + """ + + __slots__ = ("max_reaction_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 + title: Optional[str] = None, + username: Optional[str] = None, + first_name: Optional[str] = None, + last_name: Optional[str] = None, + is_forum: Optional[bool] = None, + photo: Optional[ChatPhoto] = None, + active_usernames: Optional[Sequence[str]] = None, + birthdate: Optional[Birthdate] = None, + business_intro: Optional["BusinessIntro"] = None, + business_location: Optional["BusinessLocation"] = None, + business_opening_hours: Optional["BusinessOpeningHours"] = None, + personal_chat: Optional["Chat"] = None, + available_reactions: Optional[Sequence[ReactionType]] = None, + background_custom_emoji_id: Optional[str] = None, + profile_accent_color_id: Optional[int] = None, + profile_background_custom_emoji_id: Optional[str] = None, + emoji_status_custom_emoji_id: Optional[str] = None, + emoji_status_expiration_date: Optional[datetime] = None, + bio: Optional[str] = None, + has_private_forwards: Optional[bool] = None, + has_restricted_voice_and_video_messages: Optional[bool] = None, + join_to_send_messages: Optional[bool] = None, + join_by_request: Optional[bool] = None, + description: Optional[str] = None, + invite_link: Optional[str] = None, + pinned_message: Optional["Message"] = None, + permissions: Optional[ChatPermissions] = None, + slow_mode_delay: Optional[int] = None, + unrestrict_boost_count: Optional[int] = None, + message_auto_delete_time: Optional[int] = None, + has_aggressive_anti_spam_enabled: Optional[bool] = None, + has_hidden_members: Optional[bool] = None, + has_protected_content: Optional[bool] = None, + has_visible_history: Optional[bool] = None, + sticker_set_name: Optional[str] = None, + can_set_sticker_set: Optional[bool] = None, + custom_emoji_sticker_set_name: Optional[str] = None, + linked_chat_id: Optional[int] = None, + location: Optional[ChatLocation] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__( + id=id, + type=type, + title=title, + 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 = max_reaction_count + + self._freeze() diff --git a/telegram/constants.py b/telegram/constants.py index 8bf1f9eac54..e7ff6d03823 100644 --- a/telegram/constants.py +++ b/telegram/constants.py @@ -142,7 +142,7 @@ class _AccentColor(NamedTuple): #: :data:`telegram.__bot_api_version_info__`. #: #: .. versionadded:: 20.0 -BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=2) +BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=3) #: :obj:`str`: Telegram Bot API #: version supported by this version of `python-telegram-bot`. Also available as #: :data:`telegram.__bot_api_version__`. diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 7b5649ebea3..f51d33f45b5 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -50,8 +50,8 @@ BotShortDescription, BusinessConnection, CallbackQuery, - Chat, ChatAdministratorRights, + ChatFullInfo, ChatInviteLink, ChatMember, ChatPermissions, @@ -113,7 +113,7 @@ ) from telegram.ext import BaseRateLimiter, Defaults -HandledTypes = TypeVar("HandledTypes", bound=Union[Message, CallbackQuery, Chat]) +HandledTypes = TypeVar("HandledTypes", bound=Union[Message, CallbackQuery, ChatFullInfo]) KT = TypeVar("KT", bound=ReplyMarkup) @@ -554,7 +554,7 @@ def _insert_callback_data(self, obj: HandledTypes) -> HandledTypes: self.callback_data_cache.process_message(message=obj) return obj # type: ignore[return-value] - if isinstance(obj, Chat) and obj.pinned_message: + if isinstance(obj, ChatFullInfo) and obj.pinned_message: self.callback_data_cache.process_message(obj.pinned_message) return obj @@ -853,7 +853,7 @@ async def get_chat( pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, rate_limit_args: Optional[RLARGS] = None, - ) -> Chat: + ) -> ChatFullInfo: # We override this method to call self._insert_callback_data result = await super().get_chat( chat_id=chat_id, diff --git a/tests/test_bot.py b/tests/test_bot.py index 34f25e6ce39..efc21b54bf8 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -43,6 +43,7 @@ CallbackQuery, Chat, ChatAdministratorRights, + ChatFullInfo, ChatPermissions, Dice, InlineKeyboardButton, @@ -2921,10 +2922,10 @@ async def test_leave_chat(self, bot): await bot.leave_chat(-123456) async def test_get_chat(self, bot, super_group_id): - chat = await bot.get_chat(super_group_id) - assert chat.type == "supergroup" - assert chat.title == f">>> telegram.Bot(test) @{bot.username}" - assert chat.id == int(super_group_id) + cfi = await bot.get_chat(super_group_id) + assert cfi.type == "supergroup" + assert cfi.title == f">>> telegram.Bot(test) @{bot.username}" + assert cfi.id == int(super_group_id) async def test_get_chat_administrators(self, bot, channel_id): admins = await bot.get_chat_administrators(channel_id) @@ -3900,9 +3901,9 @@ async def test_get_chat_arbitrary_callback_data(self, channel_id, cdc_bot): ) assert data == "callback_data" - chat = await bot.get_chat(channel_id) - assert chat.pinned_message == message - assert chat.pinned_message.reply_markup == reply_markup + cfi = await bot.get_chat(channel_id) + assert cfi.pinned_message == message + assert cfi.pinned_message.reply_markup == reply_markup assert await message.unpin() # (not placed in finally block since msg can be unbound) finally: bot.callback_data_cache.clear_callback_data() @@ -3915,11 +3916,11 @@ async def test_arbitrary_callback_data_get_chat_no_pinned_message( await bot.unpin_all_chat_messages(super_group_id) try: - chat = await bot.get_chat(super_group_id) + cfi = await bot.get_chat(super_group_id) - assert isinstance(chat, Chat) - assert int(chat.id) == int(super_group_id) - assert chat.pinned_message is None + assert isinstance(cfi, ChatFullInfo) + assert int(cfi.id) == int(super_group_id) + assert cfi.pinned_message is None finally: bot.callback_data_cache.clear_callback_data() bot.callback_data_cache.clear_callback_queries() diff --git a/tests/test_chat.py b/tests/test_chat.py index 11ef38dda15..7af7a677ce0 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -16,7 +16,9 @@ # # 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,9 +37,11 @@ ReactionTypeEmoji, User, ) +from telegram._chat import _deprecated_attrs from telegram._utils.datetime import UTC, to_timestamp 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, @@ -84,6 +88,8 @@ def chat(bot): 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, ) chat.set_bot(bot) chat._unfreeze() @@ -137,6 +143,8 @@ class TestChatBase: 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" class TestChatWithoutRequest(TestChatBase): @@ -185,6 +193,8 @@ 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, } chat = Chat.de_json(json_dict, bot) @@ -230,6 +240,8 @@ def test_de_json(self, bot): 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 = { @@ -251,6 +263,15 @@ def test_de_json_localization(self, bot, raw_bot, tz_bot): 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() @@ -300,15 +321,25 @@ def test_to_dict(self, chat): 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() - - 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 == () + 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") @@ -348,10 +379,7 @@ def test_full_name(self): assert chat.full_name == "first\u2022name last\u2022name" chat = Chat(id=1, type=Chat.PRIVATE, first_name="first\u2022name") assert chat.full_name == "first\u2022name" - chat = Chat( - id=1, - type=Chat.PRIVATE, - ) + chat = Chat(id=1, type=Chat.PRIVATE) assert chat.full_name is None def test_effective_name(self): @@ -588,7 +616,7 @@ async def make_assertion(*_, **kwargs): async def test_set_permissions(self, monkeypatch, chat): async def make_assertion(*_, **kwargs): chat_id = kwargs["chat_id"] == chat.id - permissions = kwargs["permissions"] == self.permissions + permissions = kwargs["permissions"] == ChatPermissions.all_permissions() return chat_id and permissions assert check_shortcut_signature( @@ -600,7 +628,7 @@ async def make_assertion(*_, **kwargs): assert await check_defaults_handling(chat.set_permissions, chat.get_bot()) monkeypatch.setattr(chat.get_bot(), "set_chat_permissions", make_assertion) - assert await chat.set_permissions(permissions=self.permissions) + assert await chat.set_permissions(permissions=ChatPermissions.all_permissions()) async def test_set_administrator_custom_title(self, monkeypatch, chat): async def make_assertion(*_, **kwargs): diff --git a/tests/test_chatfullinfo.py b/tests/test_chatfullinfo.py new file mode 100644 index 00000000000..f42642e4ed2 --- /dev/null +++ b/tests/test_chatfullinfo.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2024 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +import datetime +import warnings + +import pytest + +from telegram import ( + Birthdate, + BusinessIntro, + BusinessLocation, + BusinessOpeningHours, + BusinessOpeningHoursInterval, + Chat, + ChatFullInfo, + ChatLocation, + ChatPermissions, + Location, + 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 + + +@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, + 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=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, + birthdate=Birthdate(1, 1), + personal_chat=TestChatInfoBase.personal_chat, + ) + chat.set_bot(bot) + chat._unfreeze() + return chat + + +class TestChatInfoBase: + 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( + 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") + + +class TestChatWithoutRequest(TestChatInfoBase): + 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): + json_dict = { + "id": self.id_, + "title": self.title, + "type": self.type_, + "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(), + "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], + "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(), + } + cfi = ChatFullInfo.de_json(json_dict, bot) + assert cfi.max_reaction_count == self.max_reaction_count + + 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 + + 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) + + def test_cfi_creation_no_warning(self, chat_full_info): + cfi = chat_full_info + with warnings.catch_warnings(): + dict = cfi.to_dict() + ChatFullInfo(**dict) diff --git a/tests/test_official/exceptions.py b/tests/test_official/exceptions.py index 89892741bd4..e6dba1dae47 100644 --- a/tests/test_official/exceptions.py +++ b/tests/test_official/exceptions.py @@ -170,6 +170,43 @@ def ignored_param_requirements(object_name: str) -> set[str]: "create_new_sticker_set": {"sticker_format"}, # removed by bot api 7.2 "StickerSet": {"is_animated", "is_video"}, # removed by bot api 7.2 "UsersShared": {"user_ids", "users"}, # removed/added by bot api 7.2 + "Chat": { + "background_custom_emoji_id", + "has_private_forwards", + "invite_link", + "has_hidden_members", + "permissions", + "custom_emoji_sticker_set_name", + "pinned_message", + "birthdate", + "emoji_status_custom_emoji_id", + "join_by_request", + "business_intro", + "business_opening_hours", + "description", + "has_protected_content", + "available_reactions", + "has_aggressive_anti_spam_enabled", + "slow_mode_delay", + "profile_background_custom_emoji_id", + "linked_chat_id", + "bio", + "accent_color_id", + "unrestrict_boost_count", + "can_set_sticker_set", + "has_restricted_voice_and_video_messages", + "emoji_status_expiration_date", + "photo", + "join_to_send_messages", + "message_auto_delete_time", + "location", + "active_usernames", + "profile_accent_color_id", + "sticker_set_name", + "has_visible_history", + "business_location", + "personal_chat", + }, # removed by bot api 7.3 }