diff --git a/telegram/bot.py b/telegram/bot.py index de445d8b467..3a316b3b3a4 100644 --- a/telegram/bot.py +++ b/telegram/bot.py @@ -1753,7 +1753,7 @@ def send_venue( :obj:`title` and :obj:`address` and optionally :obj:`foursquare_id` and :obj:`foursquare_type` or optionally :obj:`google_place_id` and :obj:`google_place_type`. - * Foursquare details and Google Pace details are mutually exclusive. However, this + * Foursquare details and Google Place details are mutually exclusive. However, this behaviour is undocumented and might be changed by Telegram. Args: @@ -2657,10 +2657,10 @@ def edit_message_caption( @log def edit_message_media( self, + media: 'InputMedia', chat_id: Union[str, int] = None, message_id: int = None, inline_message_id: int = None, - media: 'InputMedia' = None, reply_markup: InlineKeyboardMarkup = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, @@ -2673,6 +2673,8 @@ def edit_message_media( ``file_id`` or specify a URL. Args: + media (:class:`telegram.InputMedia`): An object for a new media content + of the message. chat_id (:obj:`int` | :obj:`str`, optional): Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format ``@channelusername``). @@ -2680,8 +2682,6 @@ def edit_message_media( Identifier of the message to edit. inline_message_id (:obj:`str`, optional): Required if chat_id and message_id are not specified. Identifier of the inline message. - media (:class:`telegram.InputMedia`): An object for a new media content - of the message. reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): A JSON-serialized object for an inline keyboard. timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as @@ -2691,7 +2691,7 @@ def edit_message_media( Telegram API. Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the + :class:`telegram.Message`: On success, if edited message is not an inline message, the edited Message is returned, otherwise :obj:`True` is returned. Raises: @@ -2868,7 +2868,7 @@ def get_updates( @log def set_webhook( self, - url: str = None, + url: str, certificate: FileInput = None, timeout: ODVInput[float] = DEFAULT_NONE, max_connections: int = 40, @@ -2939,10 +2939,8 @@ def set_webhook( .. _`guide to Webhooks`: https://core.telegram.org/bots/webhooks """ - data: JSONDict = {} + data: JSONDict = {'url': url} - if url is not None: - data['url'] = url if certificate: data['certificate'] = parse_file_input(certificate) if max_connections is not None: @@ -4231,7 +4229,7 @@ def set_chat_title( def set_chat_description( self, chat_id: Union[str, int], - description: str, + description: str = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, ) -> bool: @@ -4243,7 +4241,7 @@ def set_chat_description( Args: chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username of the target channel (in the format ``@channelusername``). - description (:obj:`str`): New chat description, 0-255 characters. + description (:obj:`str`, optional): New chat description, 0-255 characters. timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as the read timeout from the server (instead of the one specified during creation of the connection pool). @@ -4257,7 +4255,10 @@ def set_chat_description( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'description': description} + data: JSONDict = {'chat_id': chat_id} + + if description is not None: + data['description'] = description result = self._post('setChatDescription', data, timeout=timeout, api_kwargs=api_kwargs) diff --git a/telegram/callbackquery.py b/telegram/callbackquery.py index 9630bd46fed..011d50b555d 100644 --- a/telegram/callbackquery.py +++ b/telegram/callbackquery.py @@ -319,7 +319,7 @@ def edit_message_reply_markup( def edit_message_media( self, - media: 'InputMedia' = None, + media: 'InputMedia', reply_markup: 'InlineKeyboardMarkup' = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, @@ -337,7 +337,7 @@ def edit_message_media( :meth:`telegram.Bot.edit_message_media` and :meth:`telegram.Message.edit_media`. Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the + :class:`telegram.Message`: On success, if edited message is not an inline message, the edited Message is returned, otherwise :obj:`True` is returned. """ diff --git a/telegram/chatmember.py b/telegram/chatmember.py index 445ba35a97b..5a7af9737a2 100644 --- a/telegram/chatmember.py +++ b/telegram/chatmember.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChatMember.""" import datetime -from typing import TYPE_CHECKING, Any, Optional, ClassVar, Dict, Type +from typing import TYPE_CHECKING, Optional, ClassVar, Dict, Type from telegram import TelegramObject, User, constants from telegram.utils.helpers import from_timestamp, to_timestamp @@ -42,10 +42,10 @@ class ChatMember(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`user` and :attr:`status` are equal. - Note: - As of Bot API 5.3, :class:`ChatMember` is nothing but the base class for the subclasses + .. versionchanged:: 14.0 + As of Bot API 5.3, :class:`ChatMember` is nothing but the base class for the subclasses listed above and is no longer returned directly by :meth:`~telegram.Bot.get_chat`. - Therefore, most of the arguments and attributes were deprecated and you should no longer + Therefore, most of the arguments and attributes were removed and you should no longer use :class:`ChatMember` directly. Args: @@ -54,240 +54,14 @@ class ChatMember(TelegramObject): :attr:`~telegram.ChatMember.ADMINISTRATOR`, :attr:`~telegram.ChatMember.CREATOR`, :attr:`~telegram.ChatMember.KICKED`, :attr:`~telegram.ChatMember.LEFT`, :attr:`~telegram.ChatMember.MEMBER` or :attr:`~telegram.ChatMember.RESTRICTED`. - custom_title (:obj:`str`, optional): Owner and administrators only. - Custom title for this user. - - .. deprecated:: 13.7 - - is_anonymous (:obj:`bool`, optional): Owner and administrators only. :obj:`True`, if the - user's presence in the chat is hidden. - - .. deprecated:: 13.7 - - until_date (:class:`datetime.datetime`, optional): Restricted and kicked only. Date when - restrictions will be lifted for this user. - - .. deprecated:: 13.7 - - can_be_edited (:obj:`bool`, optional): Administrators only. :obj:`True`, if the bot is - allowed to edit administrator privileges of that user. - - .. deprecated:: 13.7 - - can_manage_chat (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can access the chat event log, chat statistics, message statistics in - channels, see channel members, see anonymous administrators in supergroups and ignore - slow mode. Implied by any other administrator privilege. - - .. versionadded:: 13.4 - .. deprecated:: 13.7 - - can_manage_voice_chats (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can manage voice chats. - - .. versionadded:: 13.4 - .. deprecated:: 13.7 - - can_change_info (:obj:`bool`, optional): Administrators and restricted only. :obj:`True`, - if the user can change the chat title, photo and other settings. - - .. deprecated:: 13.7 - - can_post_messages (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can post in the channel, channels only. - - .. deprecated:: 13.7 - - can_edit_messages (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can edit messages of other users and can pin messages; channels only. - - .. deprecated:: 13.7 - - can_delete_messages (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can delete messages of other users. - - .. deprecated:: 13.7 - - can_invite_users (:obj:`bool`, optional): Administrators and restricted only. :obj:`True`, - if the user can invite new users to the chat. - - .. deprecated:: 13.7 - - can_restrict_members (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can restrict, ban or unban chat members. - - .. deprecated:: 13.7 - - can_pin_messages (:obj:`bool`, optional): Administrators and restricted only. :obj:`True`, - if the user can pin messages, groups and supergroups only. - - .. deprecated:: 13.7 - - can_promote_members (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can add new administrators with a subset of his own privileges or demote - administrators that he has promoted, directly or indirectly (promoted by administrators - that were appointed by the user). - - .. deprecated:: 13.7 - - is_member (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user is a member of - the chat at the moment of the request. - - .. deprecated:: 13.7 - - can_send_messages (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user can - send text messages, contacts, locations and venues. - - .. deprecated:: 13.7 - - can_send_media_messages (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user - can send audios, documents, photos, videos, video notes and voice notes. - - .. deprecated:: 13.7 - - can_send_polls (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user is - allowed to send polls. - - .. deprecated:: 13.7 - - can_send_other_messages (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user - can send animations, games, stickers and use inline bots. - - .. deprecated:: 13.7 - - can_add_web_page_previews (:obj:`bool`, optional): Restricted only. :obj:`True`, if user - may add web page previews to his messages. - - .. deprecated:: 13.7 Attributes: user (:class:`telegram.User`): Information about the user. status (:obj:`str`): The member's status in the chat. - custom_title (:obj:`str`): Optional. Custom title for owner and administrators. - - .. deprecated:: 13.7 - - is_anonymous (:obj:`bool`): Optional. :obj:`True`, if the user's presence in the chat is - hidden. - - .. deprecated:: 13.7 - - until_date (:class:`datetime.datetime`): Optional. Date when restrictions will be lifted - for this user. - - .. deprecated:: 13.7 - - can_be_edited (:obj:`bool`): Optional. If the bot is allowed to edit administrator - privileges of that user. - - .. deprecated:: 13.7 - - can_manage_chat (:obj:`bool`): Optional. If the administrator can access the chat event - log, chat statistics, message statistics in channels, see channel members, see - anonymous administrators in supergroups and ignore slow mode. - - .. versionadded:: 13.4 - .. deprecated:: 13.7 - - can_manage_voice_chats (:obj:`bool`): Optional. if the administrator can manage - voice chats. - - .. versionadded:: 13.4 - .. deprecated:: 13.7 - - can_change_info (:obj:`bool`): Optional. If the user can change the chat title, photo and - other settings. - - .. deprecated:: 13.7 - - can_post_messages (:obj:`bool`): Optional. If the administrator can post in the channel. - - .. deprecated:: 13.7 - - can_edit_messages (:obj:`bool`): Optional. If the administrator can edit messages of other - users. - - .. deprecated:: 13.7 - - can_delete_messages (:obj:`bool`): Optional. If the administrator can delete messages of - other users. - - .. deprecated:: 13.7 - - can_invite_users (:obj:`bool`): Optional. If the user can invite new users to the chat. - - .. deprecated:: 13.7 - - can_restrict_members (:obj:`bool`): Optional. If the administrator can restrict, ban or - unban chat members. - - .. deprecated:: 13.7 - - can_pin_messages (:obj:`bool`): Optional. If the user can pin messages. - - .. deprecated:: 13.7 - - can_promote_members (:obj:`bool`): Optional. If the administrator can add new - administrators. - - .. deprecated:: 13.7 - - is_member (:obj:`bool`): Optional. Restricted only. :obj:`True`, if the user is a member of - the chat at the moment of the request. - - .. deprecated:: 13.7 - - can_send_messages (:obj:`bool`): Optional. If the user can send text messages, contacts, - locations and venues. - - .. deprecated:: 13.7 - - can_send_media_messages (:obj:`bool`): Optional. If the user can send media messages, - implies can_send_messages. - - .. deprecated:: 13.7 - - can_send_polls (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to - send polls. - - .. deprecated:: 13.7 - - can_send_other_messages (:obj:`bool`): Optional. If the user can send animations, games, - stickers and use inline bots, implies can_send_media_messages. - - .. deprecated:: 13.7 - - can_add_web_page_previews (:obj:`bool`): Optional. If user may add web page previews to his - messages, implies can_send_media_messages - - .. deprecated:: 13.7 """ - __slots__ = ( - 'is_member', - 'can_restrict_members', - 'can_delete_messages', - 'custom_title', - 'can_be_edited', - 'can_post_messages', - 'can_send_messages', - 'can_edit_messages', - 'can_send_media_messages', - 'is_anonymous', - 'can_add_web_page_previews', - 'can_send_other_messages', - 'can_invite_users', - 'can_send_polls', - 'user', - 'can_promote_members', - 'status', - 'can_change_info', - 'can_pin_messages', - 'can_manage_chat', - 'can_manage_voice_chats', - 'until_date', - ) + __slots__ = ('user', 'status') ADMINISTRATOR: ClassVar[str] = constants.CHATMEMBER_ADMINISTRATOR """:const:`telegram.constants.CHATMEMBER_ADMINISTRATOR`""" @@ -302,58 +76,11 @@ class ChatMember(TelegramObject): RESTRICTED: ClassVar[str] = constants.CHATMEMBER_RESTRICTED """:const:`telegram.constants.CHATMEMBER_RESTRICTED`""" - def __init__( - self, - user: User, - status: str, - until_date: datetime.datetime = None, - can_be_edited: bool = None, - can_change_info: bool = None, - can_post_messages: bool = None, - can_edit_messages: bool = None, - can_delete_messages: bool = None, - can_invite_users: bool = None, - can_restrict_members: bool = None, - can_pin_messages: bool = None, - can_promote_members: bool = None, - can_send_messages: bool = None, - can_send_media_messages: bool = None, - can_send_polls: bool = None, - can_send_other_messages: bool = None, - can_add_web_page_previews: bool = None, - is_member: bool = None, - custom_title: str = None, - is_anonymous: bool = None, - can_manage_chat: bool = None, - can_manage_voice_chats: bool = None, - **_kwargs: Any, - ): - # Required + def __init__(self, user: User, status: str, **_kwargs: object): + # Required by all subclasses self.user = user self.status = status - # Optionals - self.custom_title = custom_title - self.is_anonymous = is_anonymous - self.until_date = until_date - self.can_be_edited = can_be_edited - self.can_change_info = can_change_info - self.can_post_messages = can_post_messages - self.can_edit_messages = can_edit_messages - self.can_delete_messages = can_delete_messages - self.can_invite_users = can_invite_users - self.can_restrict_members = can_restrict_members - self.can_pin_messages = can_pin_messages - self.can_promote_members = can_promote_members - self.can_send_messages = can_send_messages - self.can_send_media_messages = can_send_media_messages - self.can_send_polls = can_send_polls - self.can_send_other_messages = can_send_other_messages - self.can_add_web_page_previews = can_add_web_page_previews - self.is_member = is_member - self.can_manage_chat = can_manage_chat - self.can_manage_voice_chats = can_manage_voice_chats - self._id_attrs = (self.user, self.status) @classmethod @@ -384,7 +111,8 @@ def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() - data['until_date'] = to_timestamp(self.until_date) + if data.get('until_date', False): + data['until_date'] = to_timestamp(data['until_date']) return data @@ -398,35 +126,32 @@ class ChatMemberOwner(ChatMember): Args: user (:class:`telegram.User`): Information about the user. - custom_title (:obj:`str`, optional): Custom title for this user. - is_anonymous (:obj:`bool`, optional): :obj:`True`, if the + is_anonymous (:obj:`bool`): :obj:`True`, if the user's presence in the chat is hidden. + custom_title (:obj:`str`, optional): Custom title for this user. Attributes: status (:obj:`str`): The member's status in the chat, always :attr:`telegram.ChatMember.CREATOR`. user (:class:`telegram.User`): Information about the user. + is_anonymous (:obj:`bool`): :obj:`True`, if the user's + presence in the chat is hidden. custom_title (:obj:`str`): Optional. Custom title for this user. - is_anonymous (:obj:`bool`): Optional. :obj:`True`, if the user's - presence in the chat is hidden. """ - __slots__ = () + __slots__ = ('is_anonymous', 'custom_title') def __init__( self, user: User, + is_anonymous: bool, custom_title: str = None, - is_anonymous: bool = None, - **_kwargs: Any, + **_kwargs: object, ): - super().__init__( - status=ChatMember.CREATOR, - user=user, - custom_title=custom_title, - is_anonymous=is_anonymous, - ) + super().__init__(status=ChatMember.CREATOR, user=user) + self.is_anonymous = is_anonymous + self.custom_title = custom_title class ChatMemberAdministrator(ChatMember): @@ -437,110 +162,121 @@ class ChatMemberAdministrator(ChatMember): Args: user (:class:`telegram.User`): Information about the user. - can_be_edited (:obj:`bool`, optional): :obj:`True`, if the bot + can_be_edited (:obj:`bool`): :obj:`True`, if the bot is allowed to edit administrator privileges of that user. - custom_title (:obj:`str`, optional): Custom title for this user. - is_anonymous (:obj:`bool`, optional): :obj:`True`, if the user's + is_anonymous (:obj:`bool`): :obj:`True`, if the user's presence in the chat is hidden. - can_manage_chat (:obj:`bool`, optional): :obj:`True`, if the administrator + can_manage_chat (:obj:`bool`): :obj:`True`, if the administrator can access the chat event log, chat statistics, message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege. - can_post_messages (:obj:`bool`, optional): :obj:`True`, if the - administrator can post in the channel, channels only. - can_edit_messages (:obj:`bool`, optional): :obj:`True`, if the - administrator can edit messages of other users and can pin - messages; channels only. - can_delete_messages (:obj:`bool`, optional): :obj:`True`, if the + can_delete_messages (:obj:`bool`): :obj:`True`, if the administrator can delete messages of other users. - can_manage_voice_chats (:obj:`bool`, optional): :obj:`True`, if the + can_manage_voice_chats (:obj:`bool`): :obj:`True`, if the administrator can manage voice chats. - can_restrict_members (:obj:`bool`, optional): :obj:`True`, if the + can_restrict_members (:obj:`bool`): :obj:`True`, if the administrator can restrict, ban or unban chat members. - can_promote_members (:obj:`bool`, optional): :obj:`True`, if the administrator + can_promote_members (:obj:`bool`): :obj:`True`, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by the user). - can_change_info (:obj:`bool`, optional): :obj:`True`, if the user can change + can_change_info (:obj:`bool`): :obj:`True`, if the user can change the chat title, photo and other settings. - can_invite_users (:obj:`bool`, optional): :obj:`True`, if the user can invite + can_invite_users (:obj:`bool`): :obj:`True`, if the user can invite new users to the chat. + can_post_messages (:obj:`bool`, optional): :obj:`True`, if the + administrator can post in the channel, channels only. + can_edit_messages (:obj:`bool`, optional): :obj:`True`, if the + administrator can edit messages of other users and can pin + messages; channels only. can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to pin messages; groups and supergroups only. + custom_title (:obj:`str`, optional): Custom title for this user. Attributes: status (:obj:`str`): The member's status in the chat, always :attr:`telegram.ChatMember.ADMINISTRATOR`. user (:class:`telegram.User`): Information about the user. - can_be_edited (:obj:`bool`): Optional. :obj:`True`, if the bot + can_be_edited (:obj:`bool`): :obj:`True`, if the bot is allowed to edit administrator privileges of that user. - custom_title (:obj:`str`): Optional. Custom title for this user. - is_anonymous (:obj:`bool`): Optional. :obj:`True`, if the user's + is_anonymous (:obj:`bool`): :obj:`True`, if the user's presence in the chat is hidden. - can_manage_chat (:obj:`bool`): Optional. :obj:`True`, if the administrator + can_manage_chat (:obj:`bool`): :obj:`True`, if the administrator can access the chat event log, chat statistics, message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege. - can_post_messages (:obj:`bool`): Optional. :obj:`True`, if the - administrator can post in the channel, channels only. - can_edit_messages (:obj:`bool`): Optional. :obj:`True`, if the - administrator can edit messages of other users and can pin - messages; channels only. - can_delete_messages (:obj:`bool`): Optional. :obj:`True`, if the + can_delete_messages (:obj:`bool`): :obj:`True`, if the administrator can delete messages of other users. - can_manage_voice_chats (:obj:`bool`): Optional. :obj:`True`, if the + can_manage_voice_chats (:obj:`bool`): :obj:`True`, if the administrator can manage voice chats. - can_restrict_members (:obj:`bool`): Optional. :obj:`True`, if the + can_restrict_members (:obj:`bool`): :obj:`True`, if the administrator can restrict, ban or unban chat members. - can_promote_members (:obj:`bool`): Optional. :obj:`True`, if the administrator + can_promote_members (:obj:`bool`): :obj:`True`, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by the user). - can_change_info (:obj:`bool`): Optional. :obj:`True`, if the user can change + can_change_info (:obj:`bool`): :obj:`True`, if the user can change the chat title, photo and other settings. - can_invite_users (:obj:`bool`): Optional. :obj:`True`, if the user can invite + can_invite_users (:obj:`bool`): :obj:`True`, if the user can invite new users to the chat. + can_post_messages (:obj:`bool`): Optional. :obj:`True`, if the + administrator can post in the channel, channels only. + can_edit_messages (:obj:`bool`): Optional. :obj:`True`, if the + administrator can edit messages of other users and can pin + messages; channels only. can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to pin messages; groups and supergroups only. + custom_title (:obj:`str`): Optional. Custom title for this user. """ - __slots__ = () + __slots__ = ( + 'can_be_edited', + 'is_anonymous', + 'can_manage_chat', + 'can_delete_messages', + 'can_manage_voice_chats', + 'can_restrict_members', + 'can_promote_members', + 'can_change_info', + 'can_invite_users', + 'can_post_messages', + 'can_edit_messages', + 'can_pin_messages', + 'custom_title', + ) def __init__( self, user: User, - can_be_edited: bool = None, - custom_title: str = None, - is_anonymous: bool = None, - can_manage_chat: bool = None, + can_be_edited: bool, + is_anonymous: bool, + can_manage_chat: bool, + can_delete_messages: bool, + can_manage_voice_chats: bool, + can_restrict_members: bool, + can_promote_members: bool, + can_change_info: bool, + can_invite_users: bool, can_post_messages: bool = None, can_edit_messages: bool = None, - can_delete_messages: bool = None, - can_manage_voice_chats: bool = None, - can_restrict_members: bool = None, - can_promote_members: bool = None, - can_change_info: bool = None, - can_invite_users: bool = None, can_pin_messages: bool = None, - **_kwargs: Any, + custom_title: str = None, + **_kwargs: object, ): - super().__init__( - status=ChatMember.ADMINISTRATOR, - user=user, - can_be_edited=can_be_edited, - custom_title=custom_title, - is_anonymous=is_anonymous, - can_manage_chat=can_manage_chat, - can_post_messages=can_post_messages, - can_edit_messages=can_edit_messages, - can_delete_messages=can_delete_messages, - can_manage_voice_chats=can_manage_voice_chats, - can_restrict_members=can_restrict_members, - can_promote_members=can_promote_members, - can_change_info=can_change_info, - can_invite_users=can_invite_users, - can_pin_messages=can_pin_messages, - ) + super().__init__(status=ChatMember.ADMINISTRATOR, user=user) + self.can_be_edited = can_be_edited + self.is_anonymous = is_anonymous + self.can_manage_chat = can_manage_chat + self.can_delete_messages = can_delete_messages + self.can_manage_voice_chats = can_manage_voice_chats + self.can_restrict_members = can_restrict_members + self.can_promote_members = can_promote_members + self.can_change_info = can_change_info + self.can_invite_users = can_invite_users + self.can_post_messages = can_post_messages + self.can_edit_messages = can_edit_messages + self.can_pin_messages = can_pin_messages + self.custom_title = custom_title class ChatMemberMember(ChatMember): @@ -562,7 +298,7 @@ class ChatMemberMember(ChatMember): __slots__ = () - def __init__(self, user: User, **_kwargs: Any): + def __init__(self, user: User, **_kwargs: object): super().__init__(status=ChatMember.MEMBER, user=user) @@ -575,85 +311,93 @@ class ChatMemberRestricted(ChatMember): Args: user (:class:`telegram.User`): Information about the user. - is_member (:obj:`bool`, optional): :obj:`True`, if the user is a + is_member (:obj:`bool`): :obj:`True`, if the user is a member of the chat at the moment of the request. - can_change_info (:obj:`bool`, optional): :obj:`True`, if the user can change + can_change_info (:obj:`bool`): :obj:`True`, if the user can change the chat title, photo and other settings. - can_invite_users (:obj:`bool`, optional): :obj:`True`, if the user can invite + can_invite_users (:obj:`bool`): :obj:`True`, if the user can invite new users to the chat. - can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed + can_pin_messages (:obj:`bool`): :obj:`True`, if the user is allowed to pin messages; groups and supergroups only. - can_send_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed + can_send_messages (:obj:`bool`): :obj:`True`, if the user is allowed to send text messages, contacts, locations and venues. - can_send_media_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed + can_send_media_messages (:obj:`bool`): :obj:`True`, if the user is allowed to send audios, documents, photos, videos, video notes and voice notes. - can_send_polls (:obj:`bool`, optional): :obj:`True`, if the user is allowed + can_send_polls (:obj:`bool`): :obj:`True`, if the user is allowed to send polls. - can_send_other_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed + can_send_other_messages (:obj:`bool`): :obj:`True`, if the user is allowed to send animations, games, stickers and use inline bots. - can_add_web_page_previews (:obj:`bool`, optional): :obj:`True`, if the user is + can_add_web_page_previews (:obj:`bool`): :obj:`True`, if the user is allowed to add web page previews to their messages. - until_date (:class:`datetime.datetime`, optional): Date when restrictions + until_date (:class:`datetime.datetime`): Date when restrictions will be lifted for this user. Attributes: status (:obj:`str`): The member's status in the chat, always :attr:`telegram.ChatMember.RESTRICTED`. user (:class:`telegram.User`): Information about the user. - is_member (:obj:`bool`): Optional. :obj:`True`, if the user is a + is_member (:obj:`bool`): :obj:`True`, if the user is a member of the chat at the moment of the request. - can_change_info (:obj:`bool`): Optional. :obj:`True`, if the user can change + can_change_info (:obj:`bool`): :obj:`True`, if the user can change the chat title, photo and other settings. - can_invite_users (:obj:`bool`): Optional. :obj:`True`, if the user can invite + can_invite_users (:obj:`bool`): :obj:`True`, if the user can invite new users to the chat. - can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed + can_pin_messages (:obj:`bool`): :obj:`True`, if the user is allowed to pin messages; groups and supergroups only. - can_send_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed + can_send_messages (:obj:`bool`): :obj:`True`, if the user is allowed to send text messages, contacts, locations and venues. - can_send_media_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed + can_send_media_messages (:obj:`bool`): :obj:`True`, if the user is allowed to send audios, documents, photos, videos, video notes and voice notes. - can_send_polls (:obj:`bool`): Optional. :obj:`True`, if the user is allowed + can_send_polls (:obj:`bool`): :obj:`True`, if the user is allowed to send polls. - can_send_other_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed + can_send_other_messages (:obj:`bool`): :obj:`True`, if the user is allowed to send animations, games, stickers and use inline bots. - can_add_web_page_previews (:obj:`bool`): Optional. :obj:`True`, if the user is + can_add_web_page_previews (:obj:`bool`): :obj:`True`, if the user is allowed to add web page previews to their messages. - until_date (:class:`datetime.datetime`): Optional. Date when restrictions + until_date (:class:`datetime.datetime`): Date when restrictions will be lifted for this user. """ - __slots__ = () + __slots__ = ( + 'is_member', + 'can_change_info', + 'can_invite_users', + 'can_pin_messages', + 'can_send_messages', + 'can_send_media_messages', + 'can_send_polls', + 'can_send_other_messages', + 'can_add_web_page_previews', + 'until_date', + ) def __init__( self, user: User, - is_member: bool = None, - can_change_info: bool = None, - can_invite_users: bool = None, - can_pin_messages: bool = None, - can_send_messages: bool = None, - can_send_media_messages: bool = None, - can_send_polls: bool = None, - can_send_other_messages: bool = None, - can_add_web_page_previews: bool = None, - until_date: datetime.datetime = None, - **_kwargs: Any, + is_member: bool, + can_change_info: bool, + can_invite_users: bool, + can_pin_messages: bool, + can_send_messages: bool, + can_send_media_messages: bool, + can_send_polls: bool, + can_send_other_messages: bool, + can_add_web_page_previews: bool, + until_date: datetime.datetime, + **_kwargs: object, ): - super().__init__( - status=ChatMember.RESTRICTED, - user=user, - is_member=is_member, - can_change_info=can_change_info, - can_invite_users=can_invite_users, - can_pin_messages=can_pin_messages, - can_send_messages=can_send_messages, - can_send_media_messages=can_send_media_messages, - can_send_polls=can_send_polls, - can_send_other_messages=can_send_other_messages, - can_add_web_page_previews=can_add_web_page_previews, - until_date=until_date, - ) + super().__init__(status=ChatMember.RESTRICTED, user=user) + self.is_member = is_member + self.can_change_info = can_change_info + self.can_invite_users = can_invite_users + self.can_pin_messages = can_pin_messages + self.can_send_messages = can_send_messages + self.can_send_media_messages = can_send_media_messages + self.can_send_polls = can_send_polls + self.can_send_other_messages = can_send_other_messages + self.can_add_web_page_previews = can_add_web_page_previews + self.until_date = until_date class ChatMemberLeft(ChatMember): @@ -674,7 +418,7 @@ class ChatMemberLeft(ChatMember): __slots__ = () - def __init__(self, user: User, **_kwargs: Any): + def __init__(self, user: User, **_kwargs: object): super().__init__(status=ChatMember.LEFT, user=user) @@ -687,28 +431,20 @@ class ChatMemberBanned(ChatMember): Args: user (:class:`telegram.User`): Information about the user. - until_date (:class:`datetime.datetime`, optional): Date when restrictions + until_date (:class:`datetime.datetime`): Date when restrictions will be lifted for this user. Attributes: status (:obj:`str`): The member's status in the chat, always :attr:`telegram.ChatMember.KICKED`. user (:class:`telegram.User`): Information about the user. - until_date (:class:`datetime.datetime`): Optional. Date when restrictions + until_date (:class:`datetime.datetime`): Date when restrictions will be lifted for this user. """ - __slots__ = () + __slots__ = ('until_date',) - def __init__( - self, - user: User, - until_date: datetime.datetime = None, - **_kwargs: Any, - ): - super().__init__( - status=ChatMember.KICKED, - user=user, - until_date=until_date, - ) + def __init__(self, user: User, until_date: datetime.datetime, **_kwargs: object): + super().__init__(status=ChatMember.KICKED, user=user) + self.until_date = until_date diff --git a/telegram/forcereply.py b/telegram/forcereply.py index 64e6d2293a6..b2db0bbfe7c 100644 --- a/telegram/forcereply.py +++ b/telegram/forcereply.py @@ -33,6 +33,10 @@ class ForceReply(ReplyMarkup): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`selective` is equal. + .. versionchanged:: 14.0 + The (undocumented) argument ``force_reply`` was removed and instead :attr:`force_reply` + is now always set to :obj:`True` as expected by the Bot API. + Args: selective (:obj:`bool`, optional): Use this parameter if you want to force reply from specific users only. Targets: @@ -64,14 +68,11 @@ class ForceReply(ReplyMarkup): def __init__( self, - force_reply: bool = True, selective: bool = False, input_field_placeholder: str = None, **_kwargs: Any, ): - # Required - self.force_reply = bool(force_reply) - # Optionals + self.force_reply = True self.selective = bool(selective) self.input_field_placeholder = input_field_placeholder diff --git a/telegram/message.py b/telegram/message.py index bd80785bae2..3d68f67ad2b 100644 --- a/telegram/message.py +++ b/telegram/message.py @@ -1987,7 +1987,7 @@ def edit_caption( def edit_media( self, - media: 'InputMedia' = None, + media: 'InputMedia', reply_markup: InlineKeyboardMarkup = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, @@ -2008,14 +2008,14 @@ def edit_media( behaviour is undocumented and might be changed by Telegram. Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the + :class:`telegram.Message`: On success, if edited message is not an inline message, the edited Message is returned, otherwise ``True`` is returned. """ return self.bot.edit_message_media( + media=media, chat_id=self.chat_id, message_id=self.message_id, - media=media, reply_markup=reply_markup, timeout=timeout, api_kwargs=api_kwargs, diff --git a/telegram/passport/encryptedpassportelement.py b/telegram/passport/encryptedpassportelement.py index 700655e8cfc..afa22a190c6 100644 --- a/telegram/passport/encryptedpassportelement.py +++ b/telegram/passport/encryptedpassportelement.py @@ -52,6 +52,8 @@ class EncryptedPassportElement(TelegramObject): "identity_card", "internal_passport", "address", "utility_bill", "bank_statement", "rental_agreement", "passport_registration", "temporary_registration", "phone_number", "email". + hash (:obj:`str`): Base64-encoded element hash for using in + :class:`telegram.PassportElementErrorUnspecified`. data (:class:`telegram.PersonalDetails` | :class:`telegram.IdDocument` | \ :class:`telegram.ResidentialAddress` | :obj:`str`, optional): Decrypted or encrypted data, available for "personal_details", "passport", @@ -77,8 +79,6 @@ class EncryptedPassportElement(TelegramObject): requested for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. - hash (:obj:`str`): Base64-encoded element hash for using in - :class:`telegram.PassportElementErrorUnspecified`. bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. **kwargs (:obj:`dict`): Arbitrary keyword arguments. @@ -87,6 +87,8 @@ class EncryptedPassportElement(TelegramObject): "identity_card", "internal_passport", "address", "utility_bill", "bank_statement", "rental_agreement", "passport_registration", "temporary_registration", "phone_number", "email". + hash (:obj:`str`): Base64-encoded element hash for using in + :class:`telegram.PassportElementErrorUnspecified`. data (:class:`telegram.PersonalDetails` | :class:`telegram.IdDocument` | \ :class:`telegram.ResidentialAddress` | :obj:`str`): Optional. Decrypted or encrypted data, available for "personal_details", "passport", @@ -112,8 +114,6 @@ class EncryptedPassportElement(TelegramObject): requested for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. - hash (:obj:`str`): Base64-encoded element hash for using in - :class:`telegram.PassportElementErrorUnspecified`. bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. """ @@ -135,6 +135,7 @@ class EncryptedPassportElement(TelegramObject): def __init__( self, type: str, # pylint: disable=W0622 + hash: str, # pylint: disable=W0622 data: PersonalDetails = None, phone_number: str = None, email: str = None, @@ -143,7 +144,6 @@ def __init__( reverse_side: PassportFile = None, selfie: PassportFile = None, translation: List[PassportFile] = None, - hash: str = None, # pylint: disable=W0622 bot: 'Bot' = None, credentials: 'Credentials' = None, # pylint: disable=W0613 **_kwargs: Any, diff --git a/telegram/passport/passportelementerrors.py b/telegram/passport/passportelementerrors.py index 2ad945dd3dc..f49b9a616c9 100644 --- a/telegram/passport/passportelementerrors.py +++ b/telegram/passport/passportelementerrors.py @@ -45,7 +45,6 @@ class PassportElementError(TelegramObject): """ - # All subclasses of this class won't have _id_attrs in slots since it's added here. __slots__ = ('message', 'source', 'type') def __init__(self, source: str, type: str, message: str, **_kwargs: Any): diff --git a/telegram/passport/passportfile.py b/telegram/passport/passportfile.py index b8356acf9b5..1731569aa7c 100644 --- a/telegram/passport/passportfile.py +++ b/telegram/passport/passportfile.py @@ -72,7 +72,7 @@ def __init__( file_id: str, file_unique_id: str, file_date: int, - file_size: int = None, + file_size: int, bot: 'Bot' = None, credentials: 'FileCredentials' = None, **_kwargs: Any, diff --git a/telegram/voicechat.py b/telegram/voicechat.py index c76553d5e2f..123323f5d76 100644 --- a/telegram/voicechat.py +++ b/telegram/voicechat.py @@ -20,7 +20,7 @@ """This module contains objects related to Telegram voice chats.""" import datetime as dtm -from typing import TYPE_CHECKING, Any, Optional, List +from typing import TYPE_CHECKING, Optional, List from telegram import TelegramObject, User from telegram.utils.helpers import from_timestamp, to_timestamp @@ -40,7 +40,7 @@ class VoiceChatStarted(TelegramObject): __slots__ = () - def __init__(self, **_kwargs: Any): # skipcq: PTC-W0049 + def __init__(self, **_kwargs: object): # skipcq: PTC-W0049 pass @@ -66,7 +66,7 @@ class VoiceChatEnded(TelegramObject): __slots__ = ('duration',) - def __init__(self, duration: int, **_kwargs: Any) -> None: + def __init__(self, duration: int, **_kwargs: object) -> None: self.duration = int(duration) if duration is not None else None self._id_attrs = (self.duration,) @@ -83,25 +83,22 @@ class VoiceChatParticipantsInvited(TelegramObject): .. versionadded:: 13.4 Args: - users (List[:class:`telegram.User`]): New members that + users (List[:class:`telegram.User`], optional): New members that were invited to the voice chat. **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: - users (List[:class:`telegram.User`]): New members that + users (List[:class:`telegram.User`]): Optional. New members that were invited to the voice chat. """ __slots__ = ('users',) - def __init__(self, users: List[User], **_kwargs: Any) -> None: + def __init__(self, users: List[User] = None, **_kwargs: object) -> None: self.users = users self._id_attrs = (self.users,) - def __hash__(self) -> int: - return hash(tuple(self.users)) - @classmethod def de_json( cls, data: Optional[JSONDict], bot: 'Bot' @@ -119,9 +116,13 @@ def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() - data["users"] = [u.to_dict() for u in self.users] + if self.users is not None: + data["users"] = [u.to_dict() for u in self.users] return data + def __hash__(self) -> int: + return hash(None) if self.users is None else hash(tuple(self.users)) + class VoiceChatScheduled(TelegramObject): """This object represents a service message about a voice chat scheduled in the chat. @@ -142,7 +143,7 @@ class VoiceChatScheduled(TelegramObject): __slots__ = ('start_date',) - def __init__(self, start_date: dtm.datetime, **_kwargs: Any) -> None: + def __init__(self, start_date: dtm.datetime, **_kwargs: object) -> None: self.start_date = start_date self._id_attrs = (self.start_date,) diff --git a/tests/test_bot.py b/tests/test_bot.py index 747c5a96cc6..44f79deac71 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -1313,7 +1313,7 @@ def assertion(url, data, *args, **kwargs): monkeypatch.setattr(bot.request, 'post', assertion) - assert bot.set_webhook(drop_pending_updates=drop_pending_updates) + assert bot.set_webhook('', drop_pending_updates=drop_pending_updates) assert bot.delete_webhook(drop_pending_updates=drop_pending_updates) @flaky(3, 1) @@ -1779,7 +1779,6 @@ def test_set_chat_title(self, bot, channel_id): def test_set_chat_description(self, bot, channel_id): assert bot.set_chat_description(channel_id, 'Time: ' + str(time.time())) - # TODO: Add bot to group to test there too @flaky(3, 1) def test_pin_and_unpin_message(self, bot, super_group_id): message1 = bot.send_message(super_group_id, text="test_pin_message_1") diff --git a/tests/test_chatmember.py b/tests/test_chatmember.py index 62c296c37fb..3b04f0908f6 100644 --- a/tests/test_chatmember.py +++ b/tests/test_chatmember.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 inspect from copy import deepcopy import pytest @@ -34,202 +35,197 @@ Dice, ) - -@pytest.fixture(scope='class') -def user(): - return User(1, 'First name', False) - - -@pytest.fixture( - scope="class", - params=[ - (ChatMemberOwner, ChatMember.CREATOR), - (ChatMemberAdministrator, ChatMember.ADMINISTRATOR), - (ChatMemberMember, ChatMember.MEMBER), - (ChatMemberRestricted, ChatMember.RESTRICTED), - (ChatMemberLeft, ChatMember.LEFT), - (ChatMemberBanned, ChatMember.KICKED), - ], - ids=[ - ChatMember.CREATOR, - ChatMember.ADMINISTRATOR, - ChatMember.MEMBER, - ChatMember.RESTRICTED, - ChatMember.LEFT, - ChatMember.KICKED, +ignored = ['self', '_kwargs'] + + +class CMDefaults: + user = User(1, 'First name', False) + custom_title: str = 'PTB' + is_anonymous: bool = True + until_date: datetime.datetime = to_timestamp(datetime.datetime.utcnow()) + can_be_edited: bool = False + can_change_info: bool = True + can_post_messages: bool = True + can_edit_messages: bool = True + can_delete_messages: bool = True + can_invite_users: bool = True + can_restrict_members: bool = True + can_pin_messages: bool = True + can_promote_members: bool = True + can_send_messages: bool = True + can_send_media_messages: bool = True + can_send_polls: bool = True + can_send_other_messages: bool = True + can_add_web_page_previews: bool = True + is_member: bool = True + can_manage_chat: bool = True + can_manage_voice_chats: bool = True + + +def chat_member_owner(): + return ChatMemberOwner(CMDefaults.user, CMDefaults.is_anonymous, CMDefaults.custom_title) + + +def chat_member_administrator(): + return ChatMemberAdministrator( + CMDefaults.user, + CMDefaults.can_be_edited, + CMDefaults.is_anonymous, + CMDefaults.can_manage_chat, + CMDefaults.can_delete_messages, + CMDefaults.can_manage_voice_chats, + CMDefaults.can_restrict_members, + CMDefaults.can_promote_members, + CMDefaults.can_change_info, + CMDefaults.can_invite_users, + CMDefaults.can_post_messages, + CMDefaults.can_edit_messages, + CMDefaults.can_pin_messages, + CMDefaults.custom_title, + ) + + +def chat_member_member(): + return ChatMemberMember(CMDefaults.user) + + +def chat_member_restricted(): + return ChatMemberRestricted( + CMDefaults.user, + CMDefaults.is_member, + CMDefaults.can_change_info, + CMDefaults.can_invite_users, + CMDefaults.can_pin_messages, + CMDefaults.can_send_messages, + CMDefaults.can_send_media_messages, + CMDefaults.can_send_polls, + CMDefaults.can_send_other_messages, + CMDefaults.can_add_web_page_previews, + CMDefaults.until_date, + ) + + +def chat_member_left(): + return ChatMemberLeft(CMDefaults.user) + + +def chat_member_banned(): + return ChatMemberBanned(CMDefaults.user, CMDefaults.until_date) + + +def make_json_dict(instance: ChatMember, include_optional_args: bool = False) -> dict: + """Used to make the json dict which we use for testing de_json. Similar to iter_args()""" + json_dict = {'status': instance.status} + sig = inspect.signature(instance.__class__.__init__) + + for param in sig.parameters.values(): + if param.name in ignored: # ignore irrelevant params + continue + + val = getattr(instance, param.name) + # Compulsory args- + if param.default is inspect.Parameter.empty: + if hasattr(val, 'to_dict'): # convert the user object or any future ones to dict. + val = val.to_dict() + json_dict[param.name] = val + + # If we want to test all args (for de_json)- + elif param.default is not inspect.Parameter.empty and include_optional_args: + json_dict[param.name] = val + return json_dict + + +def iter_args(instance: ChatMember, de_json_inst: ChatMember, include_optional: bool = False): + """ + We accept both the regular instance and de_json created instance and iterate over them for + easy one line testing later one. + """ + yield instance.status, de_json_inst.status # yield this here cause it's not available in sig. + + sig = inspect.signature(instance.__class__.__init__) + for param in sig.parameters.values(): + if param.name in ignored: + continue + inst_at, json_at = getattr(instance, param.name), getattr(de_json_inst, param.name) + if isinstance(json_at, datetime.datetime): # Convert datetime to int + json_at = to_timestamp(json_at) + if param.default is not inspect.Parameter.empty and include_optional: + yield inst_at, json_at + elif param.default is inspect.Parameter.empty: + yield inst_at, json_at + + +@pytest.fixture +def chat_member_type(request): + return request.param() + + +@pytest.mark.parametrize( + "chat_member_type", + [ + chat_member_owner, + chat_member_administrator, + chat_member_member, + chat_member_restricted, + chat_member_left, + chat_member_banned, ], + indirect=True, ) -def chat_member_class_and_status(request): - return request.param - - -@pytest.fixture(scope='class') -def chat_member_types(chat_member_class_and_status, user): - return chat_member_class_and_status[0](status=chat_member_class_and_status[1], user=user) - - -class TestChatMember: - def test_slot_behaviour(self, chat_member_types, mro_slots): - for attr in chat_member_types.__slots__: - assert getattr(chat_member_types, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert len(mro_slots(chat_member_types)) == len( - set(mro_slots(chat_member_types)) - ), "duplicate slot" +class TestChatMemberTypes: + def test_slot_behaviour(self, chat_member_type, mro_slots): + inst = chat_member_type + for attr in inst.__slots__: + assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" + assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" + + def test_de_json_required_args(self, bot, chat_member_type): + cls = chat_member_type.__class__ + assert cls.de_json({}, bot) is None - def test_de_json_required_args(self, bot, chat_member_class_and_status, user): - cls = chat_member_class_and_status[0] - status = chat_member_class_and_status[1] + json_dict = make_json_dict(chat_member_type) + const_chat_member = ChatMember.de_json(json_dict, bot) - assert cls.de_json({}, bot) is None + assert isinstance(const_chat_member, ChatMember) + assert isinstance(const_chat_member, cls) + for chat_mem_type_at, const_chat_mem_at in iter_args(chat_member_type, const_chat_member): + assert chat_mem_type_at == const_chat_mem_at - json_dict = {'status': status, 'user': user.to_dict()} - chat_member_type = ChatMember.de_json(json_dict, bot) + def test_de_json_all_args(self, bot, chat_member_type): + json_dict = make_json_dict(chat_member_type, include_optional_args=True) + const_chat_member = ChatMember.de_json(json_dict, bot) - assert isinstance(chat_member_type, ChatMember) - assert isinstance(chat_member_type, cls) - assert chat_member_type.status == status - assert chat_member_type.user == user - - def test_de_json_all_args(self, bot, chat_member_class_and_status, user): - cls = chat_member_class_and_status[0] - status = chat_member_class_and_status[1] - time = datetime.datetime.utcnow() - - json_dict = { - 'user': user.to_dict(), - 'status': status, - 'custom_title': 'PTB', - 'is_anonymous': True, - 'until_date': to_timestamp(time), - 'can_be_edited': False, - 'can_change_info': True, - 'can_post_messages': False, - 'can_edit_messages': True, - 'can_delete_messages': True, - 'can_invite_users': False, - 'can_restrict_members': True, - 'can_pin_messages': False, - 'can_promote_members': True, - 'can_send_messages': False, - 'can_send_media_messages': True, - 'can_send_polls': False, - 'can_send_other_messages': True, - 'can_add_web_page_previews': False, - 'can_manage_chat': True, - 'can_manage_voice_chats': True, - } - chat_member_type = ChatMember.de_json(json_dict, bot) + assert isinstance(const_chat_member, ChatMember) + assert isinstance(const_chat_member, chat_member_type.__class__) + for c_mem_type_at, const_c_mem_at in iter_args(chat_member_type, const_chat_member, True): + assert c_mem_type_at == const_c_mem_at - assert isinstance(chat_member_type, ChatMember) - assert isinstance(chat_member_type, cls) - assert chat_member_type.user == user - assert chat_member_type.status == status - if chat_member_type.custom_title is not None: - assert chat_member_type.custom_title == 'PTB' - assert type(chat_member_type) in {ChatMemberOwner, ChatMemberAdministrator} - if chat_member_type.is_anonymous is not None: - assert chat_member_type.is_anonymous is True - assert type(chat_member_type) in {ChatMemberOwner, ChatMemberAdministrator} - if chat_member_type.until_date is not None: - assert type(chat_member_type) in {ChatMemberBanned, ChatMemberRestricted} - if chat_member_type.can_be_edited is not None: - assert chat_member_type.can_be_edited is False - assert type(chat_member_type) == ChatMemberAdministrator - if chat_member_type.can_change_info is not None: - assert chat_member_type.can_change_info is True - assert type(chat_member_type) in {ChatMemberAdministrator, ChatMemberRestricted} - if chat_member_type.can_post_messages is not None: - assert chat_member_type.can_post_messages is False - assert type(chat_member_type) == ChatMemberAdministrator - if chat_member_type.can_edit_messages is not None: - assert chat_member_type.can_edit_messages is True - assert type(chat_member_type) == ChatMemberAdministrator - if chat_member_type.can_delete_messages is not None: - assert chat_member_type.can_delete_messages is True - assert type(chat_member_type) == ChatMemberAdministrator - if chat_member_type.can_invite_users is not None: - assert chat_member_type.can_invite_users is False - assert type(chat_member_type) in {ChatMemberAdministrator, ChatMemberRestricted} - if chat_member_type.can_restrict_members is not None: - assert chat_member_type.can_restrict_members is True - assert type(chat_member_type) == ChatMemberAdministrator - if chat_member_type.can_pin_messages is not None: - assert chat_member_type.can_pin_messages is False - assert type(chat_member_type) in {ChatMemberAdministrator, ChatMemberRestricted} - if chat_member_type.can_promote_members is not None: - assert chat_member_type.can_promote_members is True - assert type(chat_member_type) == ChatMemberAdministrator - if chat_member_type.can_send_messages is not None: - assert chat_member_type.can_send_messages is False - assert type(chat_member_type) == ChatMemberRestricted - if chat_member_type.can_send_media_messages is not None: - assert chat_member_type.can_send_media_messages is True - assert type(chat_member_type) == ChatMemberRestricted - if chat_member_type.can_send_polls is not None: - assert chat_member_type.can_send_polls is False - assert type(chat_member_type) == ChatMemberRestricted - if chat_member_type.can_send_other_messages is not None: - assert chat_member_type.can_send_other_messages is True - assert type(chat_member_type) == ChatMemberRestricted - if chat_member_type.can_add_web_page_previews is not None: - assert chat_member_type.can_add_web_page_previews is False - assert type(chat_member_type) == ChatMemberRestricted - if chat_member_type.can_manage_chat is not None: - assert chat_member_type.can_manage_chat is True - assert type(chat_member_type) == ChatMemberAdministrator - if chat_member_type.can_manage_voice_chats is not None: - assert chat_member_type.can_manage_voice_chats is True - assert type(chat_member_type) == ChatMemberAdministrator - - def test_de_json_invalid_status(self, bot, user): - json_dict = {'status': 'invalid', 'user': user.to_dict()} + def test_de_json_invalid_status(self, chat_member_type, bot): + json_dict = {'status': 'invalid', 'user': CMDefaults.user.to_dict()} chat_member_type = ChatMember.de_json(json_dict, bot) assert type(chat_member_type) is ChatMember assert chat_member_type.status == 'invalid' - def test_de_json_subclass(self, chat_member_class_and_status, bot, chat_id, user): + def test_de_json_subclass(self, chat_member_type, bot, chat_id): """This makes sure that e.g. ChatMemberAdministrator(data, bot) never returns a - ChatMemberKicked instance.""" - cls = chat_member_class_and_status[0] - time = datetime.datetime.utcnow() - json_dict = { - 'user': user.to_dict(), - 'status': 'status', - 'custom_title': 'PTB', - 'is_anonymous': True, - 'until_date': to_timestamp(time), - 'can_be_edited': False, - 'can_change_info': True, - 'can_post_messages': False, - 'can_edit_messages': True, - 'can_delete_messages': True, - 'can_invite_users': False, - 'can_restrict_members': True, - 'can_pin_messages': False, - 'can_promote_members': True, - 'can_send_messages': False, - 'can_send_media_messages': True, - 'can_send_polls': False, - 'can_send_other_messages': True, - 'can_add_web_page_previews': False, - 'can_manage_chat': True, - 'can_manage_voice_chats': True, - } + ChatMemberBanned instance.""" + cls = chat_member_type.__class__ + json_dict = make_json_dict(chat_member_type, True) assert type(cls.de_json(json_dict, bot)) is cls - def test_to_dict(self, chat_member_types, user): - chat_member_dict = chat_member_types.to_dict() + def test_to_dict(self, chat_member_type): + chat_member_dict = chat_member_type.to_dict() assert isinstance(chat_member_dict, dict) - assert chat_member_dict['status'] == chat_member_types.status - assert chat_member_dict['user'] == user.to_dict() - - def test_equality(self, chat_member_types, user): - a = ChatMember(status='status', user=user) - b = ChatMember(status='status', user=user) - c = chat_member_types - d = deepcopy(chat_member_types) + assert chat_member_dict['status'] == chat_member_type.status + assert chat_member_dict['user'] == chat_member_type.user.to_dict() + + def test_equality(self, chat_member_type): + a = ChatMember(status='status', user=CMDefaults.user) + b = ChatMember(status='status', user=CMDefaults.user) + c = chat_member_type + d = deepcopy(chat_member_type) e = Dice(4, 'emoji') assert a == b diff --git a/tests/test_chatmemberupdated.py b/tests/test_chatmemberupdated.py index 681be38edda..1a9ef5ce1bd 100644 --- a/tests/test_chatmemberupdated.py +++ b/tests/test_chatmemberupdated.py @@ -22,7 +22,14 @@ import pytest import pytz -from telegram import User, ChatMember, Chat, ChatMemberUpdated, ChatInviteLink +from telegram import ( + User, + ChatMember, + ChatMemberAdministrator, + Chat, + ChatMemberUpdated, + ChatInviteLink, +) from telegram.utils.helpers import to_timestamp @@ -43,7 +50,19 @@ def old_chat_member(user): @pytest.fixture(scope='class') def new_chat_member(user): - return ChatMember(user, TestChatMemberUpdated.new_status) + return ChatMemberAdministrator( + user, + TestChatMemberUpdated.new_status, + True, + True, + True, + True, + True, + True, + True, + True, + True, + ) @pytest.fixture(scope='class') diff --git a/tests/test_encryptedpassportelement.py b/tests/test_encryptedpassportelement.py index 225496ee453..01812d3f821 100644 --- a/tests/test_encryptedpassportelement.py +++ b/tests/test_encryptedpassportelement.py @@ -26,6 +26,7 @@ def encrypted_passport_element(): return EncryptedPassportElement( TestEncryptedPassportElement.type_, + 'this is a hash', data=TestEncryptedPassportElement.data, phone_number=TestEncryptedPassportElement.phone_number, email=TestEncryptedPassportElement.email, @@ -38,13 +39,14 @@ def encrypted_passport_element(): class TestEncryptedPassportElement: type_ = 'type' + hash = 'this is a hash' data = 'data' phone_number = 'phone_number' email = 'email' - files = [PassportFile('file_id', 50, 0)] - front_side = PassportFile('file_id', 50, 0) - reverse_side = PassportFile('file_id', 50, 0) - selfie = PassportFile('file_id', 50, 0) + files = [PassportFile('file_id', 50, 0, 25)] + front_side = PassportFile('file_id', 50, 0, 25) + reverse_side = PassportFile('file_id', 50, 0, 25) + selfie = PassportFile('file_id', 50, 0, 25) def test_slot_behaviour(self, encrypted_passport_element, mro_slots): inst = encrypted_passport_element @@ -54,6 +56,7 @@ def test_slot_behaviour(self, encrypted_passport_element, mro_slots): def test_expected_values(self, encrypted_passport_element): assert encrypted_passport_element.type == self.type_ + assert encrypted_passport_element.hash == self.hash assert encrypted_passport_element.data == self.data assert encrypted_passport_element.phone_number == self.phone_number assert encrypted_passport_element.email == self.email @@ -88,8 +91,8 @@ def test_to_dict(self, encrypted_passport_element): ) def test_equality(self): - a = EncryptedPassportElement(self.type_, data=self.data) - b = EncryptedPassportElement(self.type_, data=self.data) + a = EncryptedPassportElement(self.type_, self.hash, data=self.data) + b = EncryptedPassportElement(self.type_, self.hash, data=self.data) c = EncryptedPassportElement(self.data, '') d = PassportElementError('source', 'type', 'message') diff --git a/tests/test_forcereply.py b/tests/test_forcereply.py index 630a043e9af..7a72bce4fcb 100644 --- a/tests/test_forcereply.py +++ b/tests/test_forcereply.py @@ -26,7 +26,6 @@ @pytest.fixture(scope='class') def force_reply(): return ForceReply( - TestForceReply.force_reply, TestForceReply.selective, TestForceReply.input_field_placeholder, ) @@ -62,16 +61,16 @@ def test_to_dict(self, force_reply): assert force_reply_dict['input_field_placeholder'] == force_reply.input_field_placeholder def test_equality(self): - a = ForceReply(True, False) - b = ForceReply(False, False) - c = ForceReply(True, True) + a = ForceReply(True, 'test') + b = ForceReply(False, 'pass') + c = ForceReply(True) d = ReplyKeyboardRemove() - assert a == b - assert hash(a) == hash(b) + assert a != b + assert hash(a) != hash(b) - assert a != c - assert hash(a) != hash(c) + assert a == c + assert hash(a) == hash(c) assert a != d assert hash(a) != hash(d) diff --git a/tests/test_inputmedia.py b/tests/test_inputmedia.py index 582e0a223d5..f01fb6e493f 100644 --- a/tests/test_inputmedia.py +++ b/tests/test_inputmedia.py @@ -638,9 +638,9 @@ def build_media(parse_mode, med_type): message = default_bot.send_photo(chat_id, photo) message = default_bot.edit_message_media( + build_media(parse_mode=ParseMode.HTML, med_type=media_type), message.chat_id, message.message_id, - media=build_media(parse_mode=ParseMode.HTML, med_type=media_type), ) assert message.caption == test_caption assert message.caption_entities == test_entities @@ -649,9 +649,9 @@ def build_media(parse_mode, med_type): message.edit_caption() message = default_bot.edit_message_media( + build_media(parse_mode=ParseMode.MARKDOWN_V2, med_type=media_type), message.chat_id, message.message_id, - media=build_media(parse_mode=ParseMode.MARKDOWN_V2, med_type=media_type), ) assert message.caption == test_caption assert message.caption_entities == test_entities @@ -660,9 +660,9 @@ def build_media(parse_mode, med_type): message.edit_caption() message = default_bot.edit_message_media( + build_media(parse_mode=None, med_type=media_type), message.chat_id, message.message_id, - media=build_media(parse_mode=None, med_type=media_type), ) assert message.caption == markdown_caption assert message.caption_entities == [] diff --git a/tests/test_official.py b/tests/test_official.py index 5217d4e6932..29a8065667e 100644 --- a/tests/test_official.py +++ b/tests/test_official.py @@ -18,6 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. import os import inspect +from typing import List import certifi import pytest @@ -40,6 +41,13 @@ 'kwargs', } +ignored_param_requirements = { # Ignore these since there's convenience params in them (eg. Venue) + 'send_location': {'latitude', 'longitude'}, + 'edit_message_live_location': {'latitude', 'longitude'}, + 'send_venue': {'latitude', 'longitude', 'title', 'address'}, + 'send_contact': {'phone_number', 'first_name'}, +} + def find_next_sibling_until(tag, name, until): for sibling in tag.next_siblings: @@ -49,7 +57,8 @@ def find_next_sibling_until(tag, name, until): return sibling -def parse_table(h4): +def parse_table(h4) -> List[List[str]]: + """Parses the Telegram doc table and has an output of a 2D list.""" table = find_next_sibling_until(h4, 'table', h4.find_next_sibling('h4')) if not table: return [] @@ -60,8 +69,8 @@ def parse_table(h4): def check_method(h4): - name = h4.text - method = getattr(telegram.Bot, name) + name = h4.text # name of the method in telegram's docs. + method = getattr(telegram.Bot, name) # Retrieve our lib method table = parse_table(h4) # Check arguments based on source @@ -71,8 +80,11 @@ def check_method(h4): for parameter in table: param = sig.parameters.get(parameter[0]) assert param is not None, f"Parameter {parameter[0]} not found in {method.__name__}" + # TODO: Check type via docstring - # TODO: Check if optional or required + assert check_required_param( + parameter, param.name, sig, method.__name__ + ), f'Param {param.name!r} of method {method.__name__!r} requirement mismatch!' checked.append(parameter[0]) ignored = IGNORED_PARAMETERS.copy() @@ -91,8 +103,6 @@ def check_method(h4): ] ): ignored |= {'filename'} # Convenience parameter - elif name == 'setGameScore': - ignored |= {'edit_message'} # TODO: Now deprecated, so no longer in telegrams docs elif name == 'sendContact': ignored |= {'contact'} # Added for ease of use elif name in ['sendLocation', 'editMessageLiveLocation']: @@ -113,7 +123,7 @@ def check_object(h4): # Check arguments based on source. Makes sure to only check __init__'s signature & nothing else sig = inspect.signature(obj.__init__, follow_wrapped=True) - checked = [] + checked = set() for parameter in table: field = parameter[0] if field == 'from': @@ -124,18 +134,22 @@ def check_object(h4): or name.startswith('BotCommandScope') ) and field == 'type': continue - elif (name.startswith('ChatMember')) and field == 'status': + elif (name.startswith('ChatMember')) and field == 'status': # We autofill the status continue elif ( name.startswith('PassportElementError') and field == 'source' ) or field == 'remove_keyboard': continue + elif name.startswith('ForceReply') and field == 'force_reply': # this param is always True + continue param = sig.parameters.get(field) assert param is not None, f"Attribute {field} not found in {obj.__name__}" # TODO: Check type via docstring - # TODO: Check if optional or required - checked.append(field) + assert check_required_param( + parameter, field, sig, obj.__name__ + ), f"{obj.__name__!r} parameter {param.name!r} requirement mismatch" + checked.add(field) ignored = IGNORED_PARAMETERS.copy() if name == 'InputFile': @@ -144,33 +158,8 @@ def check_object(h4): ignored |= {'id', 'type'} # attributes common to all subclasses if name == 'ChatMember': ignored |= {'user', 'status'} # attributes common to all subclasses - if name == 'ChatMember': - ignored |= { - 'can_add_web_page_previews', # for backwards compatibility - 'can_be_edited', - 'can_change_info', - 'can_delete_messages', - 'can_edit_messages', - 'can_invite_users', - 'can_manage_chat', - 'can_manage_voice_chats', - 'can_pin_messages', - 'can_post_messages', - 'can_promote_members', - 'can_restrict_members', - 'can_send_media_messages', - 'can_send_messages', - 'can_send_other_messages', - 'can_send_polls', - 'custom_title', - 'is_anonymous', - 'is_member', - 'until_date', - } if name == 'BotCommandScope': ignored |= {'type'} # attributes common to all subclasses - elif name == 'User': - ignored |= {'type'} # TODO: Deprecation elif name in ('PassportFile', 'EncryptedPassportElement'): ignored |= {'credentials'} elif name == 'PassportElementError': @@ -181,6 +170,26 @@ def check_object(h4): assert (sig.parameters.keys() ^ checked) - ignored == set() +def check_required_param( + param_desc: List[str], param_name: str, sig: inspect.Signature, method_or_obj_name: str +) -> bool: + """Checks if the method/class parameter is a required/optional param as per Telegram docs.""" + if len(param_desc) == 4: # this means that there is a dedicated 'Required' column present. + # Handle cases where we provide convenience intentionally- + if param_name in ignored_param_requirements.get(method_or_obj_name, {}): + return True + is_required = True if param_desc[2] in {'Required', 'Yes'} else False + is_ours_required = sig.parameters[param_name].default is inspect.Signature.empty + return is_required is is_ours_required + + if len(param_desc) == 3: # The docs mention the requirement in the description for classes... + if param_name in ignored_param_requirements.get(method_or_obj_name, {}): + return True + is_required = False if param_desc[2].split('.', 1)[0] == 'Optional' else True + is_ours_required = sig.parameters[param_name].default is inspect.Signature.empty + return is_required is is_ours_required + + argvalues = [] names = [] http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) diff --git a/tests/test_passport.py b/tests/test_passport.py index eeeb574ecb3..2b86ed3b296 100644 --- a/tests/test_passport.py +++ b/tests/test_passport.py @@ -47,9 +47,11 @@ { 'data': 'QRfzWcCN4WncvRO3lASG+d+c5gzqXtoCinQ1PgtYiZMKXCksx9eB9Ic1bOt8C/un9/XaX220PjJSO7Kuba+nXXC51qTsjqP9rnLKygnEIWjKrfiDdklzgcukpRzFSjiOAvhy86xFJZ1PfPSrFATy/Gp1RydLzbrBd2ZWxZqXrxcMoA0Q2UTTFXDoCYerEAiZoD69i79tB/6nkLBcUUvN5d52gKd/GowvxWqAAmdO6l1N7jlo6aWjdYQNBAK1KHbJdbRZMJLxC1MqMuZXAYrPoYBRKr5xAnxDTmPn/LEZKLc3gwwZyEgR5x7e9jp5heM6IEMmsv3O/6SUeEQs7P0iVuRSPLMJLfDdwns8Tl3fF2M4IxKVovjCaOVW+yHKsADDAYQPzzH2RcrWVD0TP5I64mzpK64BbTOq3qm3Hn51SV9uA/+LvdGbCp7VnzHx4EdUizHsVyilJULOBwvklsrDRvXMiWmh34ZSR6zilh051tMEcRf0I+Oe7pIxVJd/KKfYA2Z/eWVQTCn5gMuAInQNXFSqDIeIqBX+wca6kvOCUOXB7J2uRjTpLaC4DM9s/sNjSBvFixcGAngt+9oap6Y45rQc8ZJaNN/ALqEJAmkphW8=', 'type': 'personal_details', + 'hash': 'What to put here?', }, { 'reverse_side': { + 'file_size': 32424112, 'file_date': 1534074942, 'file_id': 'DgADBAADNQQAAtoagFPf4wwmFZdmyQI', 'file_unique_id': 'adc3145fd2e84d95b64d68eaa22aa33e', @@ -82,6 +84,7 @@ 'file_unique_id': 'd4e390cca57b4da5a65322b304762a12', }, 'data': 'eJUOFuY53QKmGqmBgVWlLBAQCUQJ79n405SX6M5aGFIIodOPQqnLYvMNqTwTrXGDlW+mVLZcbu+y8luLVO8WsJB/0SB7q5WaXn/IMt1G9lz5G/KMLIZG/x9zlnimsaQLg7u8srG6L4KZzv+xkbbHjZdETrxU8j0N/DoS4HvLMRSJAgeFUrY6v2YW9vSRg+fSxIqQy1jR2VKpzAT8OhOz7A==', + 'hash': 'We seriously need to improve this mess! took so long to debug!', }, { 'translation': [ @@ -113,12 +116,14 @@ }, ], 'type': 'utility_bill', + 'hash': 'Wow over 30 minutes spent debugging passport stuff.', }, { 'data': 'j9SksVkSj128DBtZA+3aNjSFNirzv+R97guZaMgae4Gi0oDVNAF7twPR7j9VSmPedfJrEwL3O889Ei+a5F1xyLLyEI/qEBljvL70GFIhYGitS0JmNabHPHSZrjOl8b4s/0Z0Px2GpLO5siusTLQonimdUvu4UPjKquYISmlKEKhtmGATy+h+JDjNCYuOkhakeNw0Rk0BHgj0C3fCb7WZNQSyVb+2GTu6caR6eXf/AFwFp0TV3sRz3h0WIVPW8bna', 'type': 'address', + 'hash': 'at least I get the pattern now', }, - {'email': 'fb3e3i47zt@dispostable.com', 'type': 'email'}, + {'email': 'fb3e3i47zt@dispostable.com', 'type': 'email', 'hash': 'this should be it.'}, ], } @@ -126,13 +131,18 @@ @pytest.fixture(scope='function') def all_passport_data(): return [ - {'type': 'personal_details', 'data': RAW_PASSPORT_DATA['data'][0]['data']}, + { + 'type': 'personal_details', + 'data': RAW_PASSPORT_DATA['data'][0]['data'], + 'hash': 'what to put here?', + }, { 'type': 'passport', 'data': RAW_PASSPORT_DATA['data'][1]['data'], 'front_side': RAW_PASSPORT_DATA['data'][1]['front_side'], 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie'], 'translation': RAW_PASSPORT_DATA['data'][1]['translation'], + 'hash': 'more data arghh', }, { 'type': 'internal_passport', @@ -140,6 +150,7 @@ def all_passport_data(): 'front_side': RAW_PASSPORT_DATA['data'][1]['front_side'], 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie'], 'translation': RAW_PASSPORT_DATA['data'][1]['translation'], + 'hash': 'more data arghh', }, { 'type': 'driver_license', @@ -148,6 +159,7 @@ def all_passport_data(): 'reverse_side': RAW_PASSPORT_DATA['data'][1]['reverse_side'], 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie'], 'translation': RAW_PASSPORT_DATA['data'][1]['translation'], + 'hash': 'more data arghh', }, { 'type': 'identity_card', @@ -156,35 +168,49 @@ def all_passport_data(): 'reverse_side': RAW_PASSPORT_DATA['data'][1]['reverse_side'], 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie'], 'translation': RAW_PASSPORT_DATA['data'][1]['translation'], + 'hash': 'more data arghh', }, { 'type': 'utility_bill', 'files': RAW_PASSPORT_DATA['data'][2]['files'], 'translation': RAW_PASSPORT_DATA['data'][2]['translation'], + 'hash': 'more data arghh', }, { 'type': 'bank_statement', 'files': RAW_PASSPORT_DATA['data'][2]['files'], 'translation': RAW_PASSPORT_DATA['data'][2]['translation'], + 'hash': 'more data arghh', }, { 'type': 'rental_agreement', 'files': RAW_PASSPORT_DATA['data'][2]['files'], 'translation': RAW_PASSPORT_DATA['data'][2]['translation'], + 'hash': 'more data arghh', }, { 'type': 'passport_registration', 'files': RAW_PASSPORT_DATA['data'][2]['files'], 'translation': RAW_PASSPORT_DATA['data'][2]['translation'], + 'hash': 'more data arghh', }, { 'type': 'temporary_registration', 'files': RAW_PASSPORT_DATA['data'][2]['files'], 'translation': RAW_PASSPORT_DATA['data'][2]['translation'], + 'hash': 'more data arghh', + }, + { + 'type': 'address', + 'data': RAW_PASSPORT_DATA['data'][3]['data'], + 'hash': 'more data arghh', + }, + {'type': 'email', 'email': 'fb3e3i47zt@dispostable.com', 'hash': 'more data arghh'}, + { + 'type': 'phone_number', + 'phone_number': 'fb3e3i47zt@dispostable.com', + 'hash': 'more data arghh', }, - {'type': 'address', 'data': RAW_PASSPORT_DATA['data'][3]['data']}, - {'type': 'email', 'email': 'fb3e3i47zt@dispostable.com'}, - {'type': 'phone_number', 'phone_number': 'fb3e3i47zt@dispostable.com'}, ] diff --git a/tests/test_update.py b/tests/test_update.py index e095541d132..a02aa56ca04 100644 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -33,7 +33,7 @@ Poll, PollOption, ChatMemberUpdated, - ChatMember, + ChatMemberOwner, ) from telegram.poll import PollAnswer from telegram.utils.helpers import from_timestamp @@ -43,8 +43,8 @@ Chat(1, 'chat'), User(1, '', False), from_timestamp(int(time.time())), - ChatMember(User(1, '', False), ChatMember.CREATOR), - ChatMember(User(1, '', False), ChatMember.CREATOR), + ChatMemberOwner(User(1, '', False), True), + ChatMemberOwner(User(1, '', False), True), ) params = [ diff --git a/tests/test_voicechat.py b/tests/test_voicechat.py index 94174bb4183..3e847f7a370 100644 --- a/tests/test_voicechat.py +++ b/tests/test_voicechat.py @@ -95,7 +95,7 @@ def test_equality(self): class TestVoiceChatParticipantsInvited: - def test_slot_behaviour(self, mro_slots): + def test_slot_behaviour(self, mro_slots, user1): action = VoiceChatParticipantsInvited([user1]) for attr in action.__slots__: assert getattr(action, attr, 'err') != 'err', f"got extra slot '{attr}'" @@ -124,7 +124,7 @@ def test_equality(self, user1, user2): a = VoiceChatParticipantsInvited([user1]) b = VoiceChatParticipantsInvited([user1]) c = VoiceChatParticipantsInvited([user1, user2]) - d = VoiceChatParticipantsInvited([user2]) + d = VoiceChatParticipantsInvited(None) e = VoiceChatStarted() assert a == b 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