From ac9543bb6dcb4030c4d2e01d94ca736cc40f5336 Mon Sep 17 00:00:00 2001 From: Harshil <37377066+harshil21@users.noreply.github.com> Date: Wed, 8 May 2024 14:31:18 -0400 Subject: [PATCH 1/9] Add the ChatFullInfo class and adjust tests --- docs/source/telegram.at-tree.rst | 1 + docs/source/telegram.chatfullinfo.rst | 6 + telegram/__init__.py | 2 + telegram/_bot.py | 11 +- telegram/_chat.py | 468 +---------------- telegram/_chatfullinfo.py | 709 ++++++++++++++++++++++++++ telegram/ext/_extbot.py | 8 +- tests/test_bot.py | 23 +- tests/test_chat.py | 247 +-------- tests/test_chatfullinfo.py | 401 +++++++++++++++ 10 files changed, 1160 insertions(+), 716 deletions(-) create mode 100644 docs/source/telegram.chatfullinfo.rst create mode 100644 telegram/_chatfullinfo.py create mode 100644 tests/test_chatfullinfo.py 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..8d83dc842b6 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -23,17 +23,12 @@ 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.helpers import escape_markdown @@ -44,10 +39,6 @@ from telegram import ( Animation, Audio, - Bot, - BusinessIntro, - BusinessLocation, - BusinessOpeningHours, ChatInviteLink, ChatMember, Contact, @@ -93,6 +84,10 @@ class Chat(TelegramObject): this field for backwards compatibility, it is available through :attr:`~telegram.TelegramObject.api_kwargs`. + .. versionchanged:: NEXT.VERSION + Most of the attributes in this class has now moved to :class:`telegram.ChatFullInfo` in + accordance with Bot API 7.3. + 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. @@ -105,157 +100,10 @@ class Chat(TelegramObject): 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`. - bio (:obj:`str`, optional): Bio of the other party in a private chat. Returned only in - :meth:`telegram.Bot.get_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. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 13.9 - description (:obj:`str`, optional): Description, for groups, supergroups and channel chats. - Returned only in :meth:`telegram.Bot.get_chat`. - invite_link (:obj:`str`, optional): Primary invite link, for groups, supergroups and - channel. Returned only in :meth:`telegram.Bot.get_chat`. - pinned_message (:class:`telegram.Message`, optional): The most recent pinned message - (by sending date). Returned only in :meth:`telegram.Bot.get_chat`. - permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions, - for groups and supergroups. Returned only in :meth:`telegram.Bot.get_chat`. - 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`. - 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 - 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 - 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 - sticker_set_name (:obj:`str`, optional): For supergroups, name of group sticker set. - Returned only in :meth:`telegram.Bot.get_chat`. - 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`. - 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`. - location (:class:`telegram.ChatLocation`, optional): For supergroups, the location to which - the supergroup is connected. Returned only in :meth:`telegram.Bot.get_chat`. - 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 - 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 - 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 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 Attributes: id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits @@ -269,206 +117,19 @@ class Chat(TelegramObject): 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`. - bio (:obj:`str`): Optional. Bio of the other party in a private chat. Returned only in - :meth:`telegram.Bot.get_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. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 13.9 - description (:obj:`str`): Optional. Description, for groups, supergroups and channel chats. - Returned only in :meth:`telegram.Bot.get_chat`. - invite_link (:obj:`str`): Optional. Primary invite link, for groups, supergroups and - channel. Returned only in :meth:`telegram.Bot.get_chat`. - pinned_message (:class:`telegram.Message`): Optional. The most recent pinned message - (by sending date). Returned only in :meth:`telegram.Bot.get_chat`. - permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions, - for groups and supergroups. Returned only in :meth:`telegram.Bot.get_chat`. - 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`. - 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 - 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 - 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 - sticker_set_name (:obj:`str`): Optional. For supergroups, name of Group sticker set. - Returned only in :meth:`telegram.Bot.get_chat`. - 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`. - 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`. - location (:class:`telegram.ChatLocation`): Optional. For supergroups, the location to which - the supergroup is connected. Returned only in :meth:`telegram.Bot.get_chat`. - 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 - 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 - 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 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - - .. _topics: https://telegram.org/blog/topics-in-groups-collectible-usernames#topics-in-groups - .. _accent colors: https://core.telegram.org/bots/api#accent-colors """ __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", ) @@ -494,42 +155,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, - 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, ): @@ -542,48 +168,7 @@ 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 self._id_attrs = (self.id,) @@ -630,51 +215,6 @@ def link(self) -> Optional[str]: 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"] = cls.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: diff --git a/telegram/_chatfullinfo.py b/telegram/_chatfullinfo.py new file mode 100644 index 00000000000..e5a0297134d --- /dev/null +++ b/telegram/_chatfullinfo.py @@ -0,0 +1,709 @@ +#!/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 html import escape +from typing import TYPE_CHECKING, Final, Optional, Sequence, Tuple + +from telegram import constants +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._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.types import JSONDict +from telegram.helpers import escape_markdown +from telegram.helpers import mention_html as helpers_mention_html +from telegram.helpers import mention_markdown as helpers_mention_markdown + +if TYPE_CHECKING: + from telegram import Bot, BusinessIntro, BusinessLocation, BusinessOpeningHours, Message + + +class ChatFullInfo(TelegramObject): + """ + 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:`id` is equal. + + .. versionadded:: NEXT.VERSION + + 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`. + accent_color_id (:obj:`int`): 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 + + .. versionchanged:: NEXT.VERSION + This attribute is now required as per Bot API 7.3. + max_reaction_count (:obj:`int`): The maximum number of reactions that can be set on a + message in the chat. + + .. versionadded:: NEXT.VERSION + 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. + max_reaction_count (:obj:`int`): The maximum number of reactions that can be set on a + message in the chat. + + .. versionadded:: NEXT.VERSION + 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 + 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. + + 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_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. + 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. + 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 need to be approved by supergroup administrators. + + .. versionadded:: 20.0 + 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 + 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. + + .. versionadded:: 20.0 + 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 + 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 + 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 + 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 + 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 + birthdate (:obj:`telegram.Birthdate`, optional): For private chats, + the date of birth of the user. + + .. versionadded:: 21.1 + personal_chat (:obj:`telegram.Chat`, optional): For private chats, the personal channel of + the user. + + .. versionadded:: 21.1 + + 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`. + accent_color_id (:obj:`int`): 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 + + .. versionchanged:: NEXT.VERSION + This attribute is now required as per Bot API 7.3. + max_reaction_count (:obj:`int`): The maximum number of reactions that can be set on a + message in the chat. + + .. versionadded:: NEXT.VERSION + + 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. + 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 + 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. + 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_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. + 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. + 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 need to be approved by supergroup administrators. + + .. versionadded:: 20.0 + 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 + 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. + 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 + 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 + 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 + 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 + 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 + 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 + birthdate (:obj:`telegram.Birthdate`): Optional. For private chats, + the date of birth of the user. + + .. versionadded:: 21.1 + personal_chat (:obj:`telegram.Chat`): Optional. For private chats, the personal channel of + the user. + + .. versionadded:: 21.1 + + .. _topics: https://telegram.org/blog/topics-in-groups-collectible-usernames#topics-in-groups + .. _accent colors: https://core.telegram.org/bots/api#accent-colors + """ + + __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", + "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", + "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`""" + + def __init__( + self, + id: int, + type: str, + accent_color_id: int, + max_reaction_count: int, + title: Optional[str] = None, + username: Optional[str] = None, + first_name: Optional[str] = None, + last_name: Optional[str] = None, + 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, + 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["ChatFullInfo"] = None, + business_intro: Optional["BusinessIntro"] = None, + business_location: Optional["BusinessLocation"] = None, + business_opening_hours: Optional["BusinessOpeningHours"] = None, + *, + api_kwargs: Optional[JSONDict] = None, + ): + super().__init__(api_kwargs=api_kwargs) + # Required + self.id: int = id + self.type: str = enum.get_member(constants.ChatType, type, type) + self.accent_color_id = accent_color_id + self.max_reaction_count = max_reaction_count + # Optionals + self.title: Optional[str] = title + self.username: Optional[str] = username + self.first_name: Optional[str] = first_name + self.last_name: Optional[str] = last_name + self.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.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["ChatFullInfo"] = personal_chat + self.business_intro: Optional["BusinessIntro"] = business_intro + self.business_location: Optional["BusinessLocation"] = business_location + self.business_opening_hours: Optional["BusinessOpeningHours"] = business_opening_hours + + self._id_attrs = (self.id,) + + self._freeze() + + @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 + ) + + 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) + + @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`. + """ + if self.title is not None: + return self.title + if self.full_name is not None: + return self.full_name + return None + + @property + def full_name(self) -> Optional[str]: + """ + :obj:`str`: Convenience property. If :attr:`first_name` is not :obj:`None`, gives + :attr:`first_name` followed by (if available) :attr:`last_name`. + + Note: + :attr:`full_name` will always be :obj:`None`, if the chat is a (super)group or + channel. + """ + if not self.first_name: + return None + if self.last_name: + return f"{self.first_name} {self.last_name}" + return self.first_name + + @property + def link(self) -> Optional[str]: + """:obj:`str`: Convenience property. If the chat has a :attr:`username`, returns a t.me + link of the chat. + """ + if self.username: + return f"https://t.me/{self.username}" + return None + + def mention_markdown(self, name: Optional[str] = None) -> str: + """ + Note: + :tg-const:`telegram.constants.ParseMode.MARKDOWN` is a legacy mode, retained by + Telegram for backward compatibility. You should use :meth:`mention_markdown_v2` + instead. + + Args: + name (:obj:`str`): The name used as a link for the chat. Defaults to :attr:`full_name`. + + Returns: + :obj:`str`: The inline mention for the chat as 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`. + + """ + if self.type == self.PRIVATE: + if name: + return helpers_mention_markdown(self.id, name) + if self.full_name: + return helpers_mention_markdown(self.id, self.full_name) + raise TypeError("Can not create a mention to a private chat without first name") + if self.username: + if name: + return f"[{name}]({self.link})" + if self.title: + return f"[{self.title}]({self.link})" + raise TypeError("Can not create a mention to a public chat without title") + raise TypeError("Can not create a mention to a private group chat") + + def mention_markdown_v2(self, name: Optional[str] = None) -> str: + """ + Args: + name (:obj:`str`): The name used as a link for the chat. Defaults to :attr:`full_name`. + + Returns: + :obj:`str`: The inline mention for the chat as 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`. + + """ + if self.type == self.PRIVATE: + if name: + return helpers_mention_markdown(self.id, name, version=2) + if self.full_name: + return helpers_mention_markdown(self.id, self.full_name, version=2) + raise TypeError("Can not create a mention to a private chat without first name") + if self.username: + if name: + return f"[{escape_markdown(name, version=2)}]({self.link})" + if self.title: + return f"[{escape_markdown(self.title, version=2)}]({self.link})" + raise TypeError("Can not create a mention to a public chat without title") + raise TypeError("Can not create a mention to a private group chat") + + def mention_html(self, name: Optional[str] = None) -> str: + """ + Args: + name (:obj:`str`): The name used as a link for the chat. Defaults to :attr:`full_name`. + + Returns: + :obj:`str`: The inline mention for the chat as HTML. + + Raises: + :exc:`TypeError`: If the chat is a private chat and neither the :paramref:`name` + nor the :attr:`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`. + + """ + if self.type == self.PRIVATE: + if name: + return helpers_mention_html(self.id, name) + if self.full_name: + return helpers_mention_html(self.id, self.full_name) + raise TypeError("Can not create a mention to a private chat without first name") + if self.username: + if name: + return f'{escape(name)}' + if self.title: + return f'{escape(self.title)}' + raise TypeError("Can not create a mention to a public chat without title") + raise TypeError("Can not create a mention to a private group chat") 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..a1679a4fd8b 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -16,26 +16,10 @@ # # 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 pytest -from telegram import ( - Birthdate, - Bot, - BusinessIntro, - BusinessLocation, - BusinessOpeningHours, - BusinessOpeningHoursInterval, - Chat, - ChatLocation, - ChatPermissions, - Location, - ReactionTypeCustomEmoji, - ReactionTypeEmoji, - User, -) -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 tests.auxil.bot_method_checks import ( @@ -53,37 +37,9 @@ 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, + first_name=TestChatBase.first_name, + last_name=TestChatBase.last_name, 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, ) chat.set_bot(bot) chat._unfreeze() @@ -95,48 +51,9 @@ 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 + first_name = "first" + last_name = "last" 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(TestChatBase): @@ -151,40 +68,9 @@ 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, } chat = Chat.de_json(json_dict, bot) @@ -192,64 +78,9 @@ 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 - - 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 + assert chat.first_name == self.first_name + assert chat.last_name == self.last_name def test_to_dict(self, chat): chat_dict = chat.to_dict() @@ -259,56 +90,9 @@ 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() - - 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_enum_init(self): chat = Chat(id=1, type="foo") @@ -348,10 +132,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 +369,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 +381,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..b8f8634aa59 --- /dev/null +++ b/tests/test_chatfullinfo.py @@ -0,0 +1,401 @@ +#!/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 pytest + +from telegram import ( + Birthdate, + BusinessIntro, + BusinessLocation, + BusinessOpeningHours, + BusinessOpeningHoursInterval, + Chat, + ChatFullInfo, + ChatLocation, + ChatPermissions, + Location, + ReactionTypeCustomEmoji, + ReactionTypeEmoji, + User, +) +from telegram._utils.datetime import UTC, to_timestamp +from telegram.constants import ChatType, 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.id == self.id_ + assert cfi.title == self.title + assert cfi.type == self.type_ + assert cfi.accent_color_id == self.accent_color_id + assert cfi.max_reaction_count == self.max_reaction_count + 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.api_kwargs == { + "all_members_are_administrators": self.all_members_are_administrators + } + 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.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 + + 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), + } + chat_bot = ChatFullInfo.de_json(json_dict, bot) + chat_bot_raw = ChatFullInfo.de_json(json_dict, raw_bot) + chat_bot_tz = ChatFullInfo.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_to_dict(self, chat_full_info): + cfi = chat_full_info + cfi_dict = cfi.to_dict() + + assert isinstance(cfi_dict, dict) + assert cfi_dict["id"] == cfi.id + assert cfi_dict["title"] == cfi.title + assert cfi_dict["type"] == cfi.type + assert cfi_dict["accent_color_id"] == cfi.accent_color_id + assert cfi_dict["max_reaction_count"] == cfi.max_reaction_count + 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["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() + + def test_always_tuples_attributes(self): + cfi = ChatFullInfo( + id=123, + title="title", + type=ChatFullInfo.PRIVATE, + accent_color_id=1, + max_reaction_count=2, + ) + assert isinstance(cfi.active_usernames, tuple) + assert cfi.active_usernames == () + + def test_enum_init(self): + cfi = ChatFullInfo(1, "foo", 1, 2) + assert cfi.type == "foo" + cfi = ChatFullInfo(1, "private", 1, 2) + assert cfi.type is ChatType.PRIVATE + + def test_equality(self): + a = ChatFullInfo(self.id_, self.type_, accent_color_id=1, max_reaction_count=2) + b = ChatFullInfo(self.id_, self.type_, accent_color_id=1, max_reaction_count=2) + c = ChatFullInfo(self.id_, "", accent_color_id=1, max_reaction_count=2) + d = ChatFullInfo(0, self.type_, accent_color_id=1, max_reaction_count=2) + e = User(self.id_, "", False) + + assert a == b + assert hash(a) == hash(b) + assert a is not b + + assert a == c + assert hash(a) == hash(c) + + assert a != d + assert hash(a) != hash(d) + + assert a != e + assert hash(a) != hash(e) + + def test_link(self, chat_full_info): + cfi = chat_full_info + assert cfi.link == f"https://t.me/{cfi.username}" + cfi.username = None + assert cfi.link is None + + def test_full_name(self): + cfi = ChatFullInfo( + id=1, + type=ChatFullInfo.PRIVATE, + first_name="first\u2022name", + last_name="last\u2022name", + accent_color_id=1, + max_reaction_count=2, + ) + assert cfi.full_name == "first\u2022name last\u2022name" + cfi = ChatFullInfo( + id=1, + type=ChatFullInfo.PRIVATE, + first_name="first\u2022name", + accent_color_id=1, + max_reaction_count=2, + ) + assert cfi.full_name == "first\u2022name" + cfi = ChatFullInfo( + id=1, + type=ChatFullInfo.PRIVATE, + accent_color_id=1, + max_reaction_count=2, + ) + assert cfi.full_name is None + + def test_effective_name(self): + cfi = ChatFullInfo( + id=1, + type=ChatFullInfo.PRIVATE, + first_name="first\u2022name", + accent_color_id=1, + max_reaction_count=2, + ) + assert cfi.effective_name == "first\u2022name" + cfi = ChatFullInfo( + id=1, + type=ChatFullInfo.GROUP, + title="group", + accent_color_id=1, + max_reaction_count=2, + ) + assert cfi.effective_name == "group" + cfi = ChatFullInfo( + id=1, + type=ChatFullInfo.GROUP, + first_name="first\u2022name", + title="group", + accent_color_id=1, + max_reaction_count=2, + ) + assert cfi.effective_name == "group" + cfi = ChatFullInfo(id=1, type=ChatFullInfo.GROUP, accent_color_id=1, max_reaction_count=2) + assert cfi.effective_name is None From 0ebe6e843ed113196df9676292c75bd996802633 Mon Sep 17 00:00:00 2001 From: Harshil <37377066+harshil21@users.noreply.github.com> Date: Wed, 8 May 2024 14:36:00 -0400 Subject: [PATCH 2/9] Bump bot api version number --- README.rst | 4 ++-- README_RAW.rst | 6 +++--- telegram/constants.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) 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..45f636bc2f7 100644 --- a/README_RAW.rst +++ b/README_RAW.rst @@ -14,9 +14,9 @@ :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 + :alt: Supported Bot API version .. image:: https://img.shields.io/pypi/dm/python-telegram-bot-raw :target: https://pypistats.org/packages/python-telegram-bot-raw @@ -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/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__`. From 6a5b3b840dffcc4b20b07e43af14566bdea610f5 Mon Sep 17 00:00:00 2001 From: Harshil <37377066+harshil21@users.noreply.github.com> Date: Fri, 10 May 2024 00:11:11 -0400 Subject: [PATCH 3/9] Review: Make ChatFullInfo a subclass of Chat --- telegram/_chat.py | 810 ++++++++++++++++++++++++++++++++++++- telegram/_chatfullinfo.py | 665 +++--------------------------- tests/test_chat.py | 240 ++++++++++- tests/test_chatfullinfo.py | 214 +--------- 4 files changed, 1110 insertions(+), 819 deletions(-) diff --git a/telegram/_chat.py b/telegram/_chat.py index 8d83dc842b6..20f683edf9c 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -20,25 +20,36 @@ """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 +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, @@ -65,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. @@ -84,10 +134,6 @@ class Chat(TelegramObject): this field for backwards compatibility, it is available through :attr:`~telegram.TelegramObject.api_kwargs`. - .. versionchanged:: NEXT.VERSION - Most of the attributes in this class has now moved to :class:`telegram.ChatFullInfo` in - accordance with Bot API 7.3. - 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. @@ -100,10 +146,297 @@ class Chat(TelegramObject): 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:: 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). .. 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:: 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`_ + for more details. Returned only in :meth:`telegram.Bot.get_chat`. Always returned 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`. + 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 @@ -117,22 +450,348 @@ class Chat(TelegramObject): 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:: 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). .. 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:: 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`_ + for more details. Returned only in :meth:`telegram.Bot.get_chat`. Always returned 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`. + 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 """ __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` @@ -155,7 +814,42 @@ 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, ): @@ -168,12 +862,71 @@ 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 + ) + if accent_color_id is None: + # API 7.3 requires this field, but we can't enforce it for back-compat + warn( + "Bot API 7.3 requires the `accent_color_id` field to be present. Future versions " + "of PTB will enforce this in the `ChatFullInfo` class.", + PTBDeprecationWarning, + stacklevel=2, + ) + self.accent_color_id: Optional[int] = accent_color_id + self.background_custom_emoji_id: Optional[str] = background_custom_emoji_id + self.profile_accent_color_id: Optional[int] = profile_accent_color_id + self.profile_background_custom_emoji_id: Optional[str] = profile_background_custom_emoji_id + self.unrestrict_boost_count: Optional[int] = unrestrict_boost_count + self.custom_emoji_sticker_set_name: Optional[str] = custom_emoji_sticker_set_name + self.birthdate: Optional[Birthdate] = birthdate + self.personal_chat: Optional["Chat"] = personal_chat + self.business_intro: Optional["BusinessIntro"] = business_intro + self.business_location: Optional["BusinessLocation"] = business_location + self.business_opening_hours: Optional["BusinessOpeningHours"] = business_opening_hours self._id_attrs = (self.id,) self._freeze() + def __getattribute__(self, name: str) -> Any: + if name in _deprecated_attrs and self.__class__.__name__ == "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]: """ @@ -215,6 +968,51 @@ def link(self) -> Optional[str]: 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: diff --git a/telegram/_chatfullinfo.py b/telegram/_chatfullinfo.py index e5a0297134d..34dc3bff774 100644 --- a/telegram/_chatfullinfo.py +++ b/telegram/_chatfullinfo.py @@ -19,30 +19,21 @@ # 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 html import escape -from typing import TYPE_CHECKING, Final, Optional, Sequence, Tuple +from typing import TYPE_CHECKING, Optional, Sequence -from telegram import constants 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._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.types import JSONDict -from telegram.helpers import escape_markdown -from telegram.helpers import mention_html as helpers_mention_html -from telegram.helpers import mention_markdown as helpers_mention_markdown if TYPE_CHECKING: - from telegram import Bot, BusinessIntro, BusinessLocation, BusinessOpeningHours, Message + from telegram import BusinessIntro, BusinessLocation, BusinessOpeningHours, Message -class ChatFullInfo(TelegramObject): +class ChatFullInfo(Chat): """ This object contains full information about a chat. @@ -51,387 +42,35 @@ class ChatFullInfo(TelegramObject): .. versionadded:: NEXT.VERSION - 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`. - accent_color_id (:obj:`int`): 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 + 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`. - .. versionchanged:: NEXT.VERSION - This attribute is now required as per Bot API 7.3. - max_reaction_count (:obj:`int`): The maximum number of reactions that can be set on a - message in the chat. + .. seealso:: + All arguments and attributes can be found in :class:`telegram.Chat`. - .. versionadded:: NEXT.VERSION - 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. + Args: max_reaction_count (:obj:`int`): The maximum number of reactions that can be set on a message in the chat. .. versionadded:: NEXT.VERSION - 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 - 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. - - 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_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. - 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. - 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 need to be approved by supergroup administrators. - - .. versionadded:: 20.0 - 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 - 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. - - .. versionadded:: 20.0 - 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 - 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 - 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 - 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 - 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 - birthdate (:obj:`telegram.Birthdate`, optional): For private chats, - the date of birth of the user. - - .. versionadded:: 21.1 - personal_chat (:obj:`telegram.Chat`, optional): For private chats, the personal channel of - the user. - - .. versionadded:: 21.1 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`. - accent_color_id (:obj:`int`): 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 - - .. versionchanged:: NEXT.VERSION - This attribute is now required as per Bot API 7.3. max_reaction_count (:obj:`int`): The maximum number of reactions that can be set on a message in the chat. .. versionadded:: NEXT.VERSION - - 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. - 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 - 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. - 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_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. - 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. - 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 need to be approved by supergroup administrators. - - .. versionadded:: 20.0 - 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 - 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. - 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 - 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 - 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 - 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 - 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 - 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 - birthdate (:obj:`telegram.Birthdate`): Optional. For private chats, - the date of birth of the user. - - .. versionadded:: 21.1 - personal_chat (:obj:`telegram.Chat`): Optional. For private chats, the personal channel of - the user. - - .. versionadded:: 21.1 - - .. _topics: https://telegram.org/blog/topics-in-groups-collectible-usernames#topics-in-groups - .. _accent colors: https://core.telegram.org/bots/api#accent-colors """ - __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", - "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", - "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__ = ("max_reaction_count",) def __init__( self, id: int, type: str, - accent_color_id: int, - max_reaction_count: int, + 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, @@ -467,243 +106,61 @@ def __init__( unrestrict_boost_count: Optional[int] = None, custom_emoji_sticker_set_name: Optional[str] = None, birthdate: Optional[Birthdate] = None, - personal_chat: Optional["ChatFullInfo"] = 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, ): - super().__init__(api_kwargs=api_kwargs) - # Required - self.id: int = id - self.type: str = enum.get_member(constants.ChatType, type, type) - self.accent_color_id = accent_color_id - self.max_reaction_count = max_reaction_count - # Optionals - self.title: Optional[str] = title - self.username: Optional[str] = username - self.first_name: Optional[str] = first_name - self.last_name: Optional[str] = last_name - self.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 + 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, ) - 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.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["ChatFullInfo"] = personal_chat - self.business_intro: Optional["BusinessIntro"] = business_intro - self.business_location: Optional["BusinessLocation"] = business_location - self.business_opening_hours: Optional["BusinessOpeningHours"] = business_opening_hours - self._id_attrs = (self.id,) + # Required and unique to this class- + with self._unfrozen(): + self.max_reaction_count = max_reaction_count self._freeze() - - @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 - ) - - 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) - - @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`. - """ - if self.title is not None: - return self.title - if self.full_name is not None: - return self.full_name - return None - - @property - def full_name(self) -> Optional[str]: - """ - :obj:`str`: Convenience property. If :attr:`first_name` is not :obj:`None`, gives - :attr:`first_name` followed by (if available) :attr:`last_name`. - - Note: - :attr:`full_name` will always be :obj:`None`, if the chat is a (super)group or - channel. - """ - if not self.first_name: - return None - if self.last_name: - return f"{self.first_name} {self.last_name}" - return self.first_name - - @property - def link(self) -> Optional[str]: - """:obj:`str`: Convenience property. If the chat has a :attr:`username`, returns a t.me - link of the chat. - """ - if self.username: - return f"https://t.me/{self.username}" - return None - - def mention_markdown(self, name: Optional[str] = None) -> str: - """ - Note: - :tg-const:`telegram.constants.ParseMode.MARKDOWN` is a legacy mode, retained by - Telegram for backward compatibility. You should use :meth:`mention_markdown_v2` - instead. - - Args: - name (:obj:`str`): The name used as a link for the chat. Defaults to :attr:`full_name`. - - Returns: - :obj:`str`: The inline mention for the chat as 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`. - - """ - if self.type == self.PRIVATE: - if name: - return helpers_mention_markdown(self.id, name) - if self.full_name: - return helpers_mention_markdown(self.id, self.full_name) - raise TypeError("Can not create a mention to a private chat without first name") - if self.username: - if name: - return f"[{name}]({self.link})" - if self.title: - return f"[{self.title}]({self.link})" - raise TypeError("Can not create a mention to a public chat without title") - raise TypeError("Can not create a mention to a private group chat") - - def mention_markdown_v2(self, name: Optional[str] = None) -> str: - """ - Args: - name (:obj:`str`): The name used as a link for the chat. Defaults to :attr:`full_name`. - - Returns: - :obj:`str`: The inline mention for the chat as 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`. - - """ - if self.type == self.PRIVATE: - if name: - return helpers_mention_markdown(self.id, name, version=2) - if self.full_name: - return helpers_mention_markdown(self.id, self.full_name, version=2) - raise TypeError("Can not create a mention to a private chat without first name") - if self.username: - if name: - return f"[{escape_markdown(name, version=2)}]({self.link})" - if self.title: - return f"[{escape_markdown(self.title, version=2)}]({self.link})" - raise TypeError("Can not create a mention to a public chat without title") - raise TypeError("Can not create a mention to a private group chat") - - def mention_html(self, name: Optional[str] = None) -> str: - """ - Args: - name (:obj:`str`): The name used as a link for the chat. Defaults to :attr:`full_name`. - - Returns: - :obj:`str`: The inline mention for the chat as HTML. - - Raises: - :exc:`TypeError`: If the chat is a private chat and neither the :paramref:`name` - nor the :attr:`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`. - - """ - if self.type == self.PRIVATE: - if name: - return helpers_mention_html(self.id, name) - if self.full_name: - return helpers_mention_html(self.id, self.full_name) - raise TypeError("Can not create a mention to a private chat without first name") - if self.username: - if name: - return f'{escape(name)}' - if self.title: - return f'{escape(self.title)}' - raise TypeError("Can not create a mention to a public chat without title") - raise TypeError("Can not create a mention to a private group chat") diff --git a/tests/test_chat.py b/tests/test_chat.py index a1679a4fd8b..199caba3d0b 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -17,11 +17,31 @@ # 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 Bot, Chat, ChatPermissions, ReactionTypeEmoji, User +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.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, @@ -37,9 +57,39 @@ 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, - is_forum=True, ) chat.set_bot(bot) chat._unfreeze() @@ -51,9 +101,50 @@ 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" - is_forum = True class TestChatWithoutRequest(TestChatBase): @@ -68,7 +159,40 @@ 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, } @@ -78,10 +202,67 @@ 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_to_dict(self, chat): chat_dict = chat.to_dict() @@ -90,10 +271,63 @@ 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="is deprecated and will only be"): + getattr(chat, depr_attr) + with warnings.catch_warnings(): # No warning should be raised + warnings.simplefilter("error") + chat.id + chat.first_name + + def test_required_accent_color_id(self): + with pytest.warns(PTBDeprecationWarning, match="requires the `accent_color_id` field"): + Chat(1, "type") + 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 b8f8634aa59..3af2be49e18 100644 --- a/tests/test_chatfullinfo.py +++ b/tests/test_chatfullinfo.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. import datetime +import warnings import pytest @@ -33,10 +34,10 @@ Location, ReactionTypeCustomEmoji, ReactionTypeEmoji, - User, ) +from telegram._chat import _deprecated_attrs from telegram._utils.datetime import UTC, to_timestamp -from telegram.constants import ChatType, ReactionEmoji +from telegram.constants import ReactionEmoji from tests.auxil.slots import mro_slots @@ -185,217 +186,18 @@ def test_de_json(self, bot): "personal_chat": self.personal_chat.to_dict(), } cfi = ChatFullInfo.de_json(json_dict, bot) - - assert cfi.id == self.id_ - assert cfi.title == self.title - assert cfi.type == self.type_ - assert cfi.accent_color_id == self.accent_color_id assert cfi.max_reaction_count == self.max_reaction_count - 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.api_kwargs == { - "all_members_are_administrators": self.all_members_are_administrators - } - 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.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 - - 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), - } - chat_bot = ChatFullInfo.de_json(json_dict, bot) - chat_bot_raw = ChatFullInfo.de_json(json_dict, raw_bot) - chat_bot_tz = ChatFullInfo.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_to_dict(self, chat_full_info): cfi = chat_full_info cfi_dict = cfi.to_dict() assert isinstance(cfi_dict, dict) - assert cfi_dict["id"] == cfi.id - assert cfi_dict["title"] == cfi.title - assert cfi_dict["type"] == cfi.type - assert cfi_dict["accent_color_id"] == cfi.accent_color_id assert cfi_dict["max_reaction_count"] == cfi.max_reaction_count - 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["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() - - def test_always_tuples_attributes(self): - cfi = ChatFullInfo( - id=123, - title="title", - type=ChatFullInfo.PRIVATE, - accent_color_id=1, - max_reaction_count=2, - ) - assert isinstance(cfi.active_usernames, tuple) - assert cfi.active_usernames == () - - def test_enum_init(self): - cfi = ChatFullInfo(1, "foo", 1, 2) - assert cfi.type == "foo" - cfi = ChatFullInfo(1, "private", 1, 2) - assert cfi.type is ChatType.PRIVATE - - def test_equality(self): - a = ChatFullInfo(self.id_, self.type_, accent_color_id=1, max_reaction_count=2) - b = ChatFullInfo(self.id_, self.type_, accent_color_id=1, max_reaction_count=2) - c = ChatFullInfo(self.id_, "", accent_color_id=1, max_reaction_count=2) - d = ChatFullInfo(0, self.type_, accent_color_id=1, max_reaction_count=2) - e = User(self.id_, "", False) - - assert a == b - assert hash(a) == hash(b) - assert a is not b - assert a == c - assert hash(a) == hash(c) - - assert a != d - assert hash(a) != hash(d) - - assert a != e - assert hash(a) != hash(e) - - def test_link(self, chat_full_info): + def test_attr_access_no_warning(self, chat_full_info): cfi = chat_full_info - assert cfi.link == f"https://t.me/{cfi.username}" - cfi.username = None - assert cfi.link is None - - def test_full_name(self): - cfi = ChatFullInfo( - id=1, - type=ChatFullInfo.PRIVATE, - first_name="first\u2022name", - last_name="last\u2022name", - accent_color_id=1, - max_reaction_count=2, - ) - assert cfi.full_name == "first\u2022name last\u2022name" - cfi = ChatFullInfo( - id=1, - type=ChatFullInfo.PRIVATE, - first_name="first\u2022name", - accent_color_id=1, - max_reaction_count=2, - ) - assert cfi.full_name == "first\u2022name" - cfi = ChatFullInfo( - id=1, - type=ChatFullInfo.PRIVATE, - accent_color_id=1, - max_reaction_count=2, - ) - assert cfi.full_name is None - - def test_effective_name(self): - cfi = ChatFullInfo( - id=1, - type=ChatFullInfo.PRIVATE, - first_name="first\u2022name", - accent_color_id=1, - max_reaction_count=2, - ) - assert cfi.effective_name == "first\u2022name" - cfi = ChatFullInfo( - id=1, - type=ChatFullInfo.GROUP, - title="group", - accent_color_id=1, - max_reaction_count=2, - ) - assert cfi.effective_name == "group" - cfi = ChatFullInfo( - id=1, - type=ChatFullInfo.GROUP, - first_name="first\u2022name", - title="group", - accent_color_id=1, - max_reaction_count=2, - ) - assert cfi.effective_name == "group" - cfi = ChatFullInfo(id=1, type=ChatFullInfo.GROUP, accent_color_id=1, max_reaction_count=2) - assert cfi.effective_name is None + for depr_attr in _deprecated_attrs: + with warnings.catch_warnings(): # No warning should be raised + warnings.simplefilter("error") + getattr(cfi, depr_attr) From 92f9edd5c05955c7c1cbc51aa22a504cd196a43e Mon Sep 17 00:00:00 2001 From: Harshil <37377066+harshil21@users.noreply.github.com> Date: Fri, 10 May 2024 00:15:51 -0400 Subject: [PATCH 4/9] Update test_official to add Chat attributes to exception --- tests/test_official/exceptions.py | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) 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 } From 32b91d8a5e01e9a73768e88b23121f15ac626e67 Mon Sep 17 00:00:00 2001 From: Harshil <37377066+harshil21@users.noreply.github.com> Date: Fri, 10 May 2024 00:16:10 -0400 Subject: [PATCH 5/9] Revert unintentional change to readme_raw.rst --- README_RAW.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_RAW.rst b/README_RAW.rst index 45f636bc2f7..7539408e7c8 100644 --- a/README_RAW.rst +++ b/README_RAW.rst @@ -16,7 +16,7 @@ .. 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 version + :alt: Supported Bot API versions .. image:: https://img.shields.io/pypi/dm/python-telegram-bot-raw :target: https://pypistats.org/packages/python-telegram-bot-raw From c67c213a655d0a338ce0b07f557bb0696bfd0e32 Mon Sep 17 00:00:00 2001 From: Harshil <37377066+harshil21@users.noreply.github.com> Date: Fri, 10 May 2024 00:17:39 -0400 Subject: [PATCH 6/9] fix doc build --- telegram/_chatfullinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telegram/_chatfullinfo.py b/telegram/_chatfullinfo.py index 34dc3bff774..4f7cc8ea665 100644 --- a/telegram/_chatfullinfo.py +++ b/telegram/_chatfullinfo.py @@ -38,7 +38,7 @@ 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:`id` is equal. + considered equal, if their :attr:`~telegram.Chat.id` is equal. .. versionadded:: NEXT.VERSION From 6858efa95b38e8c7da78f62f288fce8a2386b46f Mon Sep 17 00:00:00 2001 From: Harshil <37377066+harshil21@users.noreply.github.com> Date: Fri, 10 May 2024 00:29:26 -0400 Subject: [PATCH 7/9] forgot to add back one test --- tests/test_chat.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_chat.py b/tests/test_chat.py index 199caba3d0b..72d416400d3 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -263,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() From 1dcd0cbefabc9c60b0a1b17593a3890d05d44503 Mon Sep 17 00:00:00 2001 From: Harshil <37377066+harshil21@users.noreply.github.com> Date: Sat, 11 May 2024 16:51:14 -0400 Subject: [PATCH 8/9] Review: warn depr args on Chat creation & sort CFI args --- telegram/_chat.py | 18 +++++++------- telegram/_chatfullinfo.py | 48 +++++++++++++++++++------------------- tests/test_chat.py | 12 ++++++---- tests/test_chatfullinfo.py | 6 +++++ 4 files changed, 48 insertions(+), 36 deletions(-) diff --git a/telegram/_chat.py b/telegram/_chat.py index 20f683edf9c..27e7e4c9ab1 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -893,14 +893,6 @@ def __init__( self.available_reactions: Optional[Tuple[ReactionType, ...]] = parse_sequence_arg( available_reactions ) - if accent_color_id is None: - # API 7.3 requires this field, but we can't enforce it for back-compat - warn( - "Bot API 7.3 requires the `accent_color_id` field to be present. Future versions " - "of PTB will enforce this in the `ChatFullInfo` class.", - PTBDeprecationWarning, - stacklevel=2, - ) 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 @@ -913,6 +905,16 @@ def __init__( self.business_location: Optional["BusinessLocation"] = business_location self.business_opening_hours: Optional["BusinessOpeningHours"] = business_opening_hours + if self.__class__.__name__ == "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() diff --git a/telegram/_chatfullinfo.py b/telegram/_chatfullinfo.py index 4f7cc8ea665..7b7c686b086 100644 --- a/telegram/_chatfullinfo.py +++ b/telegram/_chatfullinfo.py @@ -75,41 +75,41 @@ def __init__( 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, - 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, + unrestrict_boost_count: Optional[int] = 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, - background_custom_emoji_id: Optional[str] = None, - profile_accent_color_id: Optional[int] = None, - profile_background_custom_emoji_id: Optional[str] = None, + has_protected_content: Optional[bool] = None, has_visible_history: Optional[bool] = None, - unrestrict_boost_count: Optional[int] = None, + sticker_set_name: Optional[str] = None, + can_set_sticker_set: Optional[bool] = 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, + linked_chat_id: Optional[int] = None, + location: Optional[ChatLocation] = None, *, api_kwargs: Optional[JSONDict] = None, ): diff --git a/tests/test_chat.py b/tests/test_chat.py index 72d416400d3..7af7a677ce0 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -326,16 +326,20 @@ def test_to_dict(self, chat): def test_deprecated_attributes(self, chat): for depr_attr in _deprecated_attrs: - with pytest.warns(PTBDeprecationWarning, match="is deprecated and will only be"): + 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_required_accent_color_id(self): - with pytest.warns(PTBDeprecationWarning, match="requires the `accent_color_id` field"): - Chat(1, "type") + 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") diff --git a/tests/test_chatfullinfo.py b/tests/test_chatfullinfo.py index 3af2be49e18..f42642e4ed2 100644 --- a/tests/test_chatfullinfo.py +++ b/tests/test_chatfullinfo.py @@ -201,3 +201,9 @@ def test_attr_access_no_warning(self, chat_full_info): 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) From ff632a7d6107099507b6d42862737fd117aad5f7 Mon Sep 17 00:00:00 2001 From: Harshil <37377066+harshil21@users.noreply.github.com> Date: Sun, 12 May 2024 03:10:13 -0400 Subject: [PATCH 9/9] Use `is` for comparison instead of `==` --- telegram/_chat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telegram/_chat.py b/telegram/_chat.py index 27e7e4c9ab1..9723e7edf9f 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -905,7 +905,7 @@ def __init__( self.business_location: Optional["BusinessLocation"] = business_location self.business_opening_hours: Optional["BusinessOpeningHours"] = business_opening_hours - if self.__class__.__name__ == "Chat": + if self.__class__ is Chat: for arg in _deprecated_attrs: if (val := object.__getattribute__(self, arg)) is not None and val != (): warn( @@ -920,7 +920,7 @@ def __init__( self._freeze() def __getattribute__(self, name: str) -> Any: - if name in _deprecated_attrs and self.__class__.__name__ == "Chat": + 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.", pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy