diff --git a/docs/source/inclusions/bot_methods.rst b/docs/source/inclusions/bot_methods.rst index df699e796aa..aff45cc164f 100644 --- a/docs/source/inclusions/bot_methods.rst +++ b/docs/source/inclusions/bot_methods.rst @@ -256,6 +256,35 @@
+.. raw:: html + +
+ Forum topic management + +.. list-table:: + :align: left + :widths: 1 4 + + * - :meth:`~telegram.Bot.close_forum_topic` + - Used for closing a forum topic + * - :meth:`~telegram.Bot.create_forum_topic` + - Used to create a topic + * - :meth:`~telegram.Bot.delete_forum_topic` + - Used for deleting a forum topic + * - :meth:`~telegram.Bot.edit_forum_topic` + - Used to edit a topic + * - :meth:`~telegram.Bot.reopen_forum_topic` + - Used to reopen a topic + * - :meth:`~telegram.Bot.get_forum_topic_icon_stickers` + - Used to get custom emojis to use as topic icons + * - :meth:`~telegram.Bot.unpin_all_forum_topic_messages` + - Used to unpin all messages in a forum topic + +.. raw:: html + +
+
+ .. raw:: html
diff --git a/docs/substitutions/global.rst b/docs/substitutions/global.rst index 46067cbc8a9..b06bb09db41 100644 --- a/docs/substitutions/global.rst +++ b/docs/substitutions/global.rst @@ -26,6 +26,8 @@ .. |chat_id_group| replace:: Unique identifier for the target chat or username of the target supergroup (in the format ``@supergroupusername``). +.. |message_thread_id| replace:: Unique identifier for the target message thread of the forum topic. + .. |parse_mode| replace:: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. See the constants in :class:`telegram.constants.ParseMode` for the available modes. .. |allow_sending_without_reply| replace:: Pass :obj:`True`, if the message should be sent even if the specified replied-to message is not found. diff --git a/telegram/_bot.py b/telegram/_bot.py index 72f1cc49ccd..d8825ef4881 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -6878,6 +6878,351 @@ async def create_invoice_link( api_kwargs=api_kwargs, ) + @_log + async def get_forum_topic_icon_stickers( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> List[Sticker]: + """Use this method to get custom emoji stickers, which can be used as a forum topic + icon by any user. Requires no parameters. + + .. versionadded:: 20.0 + + Returns: + List[:class:`telegram.Sticker`] + + Raises: + :class:`telegram.error.TelegramError` + + """ + result = await self._post( + "getForumTopicIconStickers", + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + return Sticker.de_list(result, self) # type: ignore[return-value, arg-type] + + @_log + async def create_forum_topic( + self, + chat_id: Union[str, int], + name: str, + icon_color: int, + icon_custom_emoji_id: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """ + Use this method to create a topic in a forum supergroup chat. The bot must be + an administrator in the chat for this to work and must have + :paramref:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights. + + .. seealso:: :meth:`telegram.Message.create_forum_topic`, + :meth:`telegram.Chat.create_forum_topic`, + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + name (:obj:`str`): New topic name, + :tg-const:`telegram.constants.TopicLimit.MIN_NAME_LENGTH`- + :tg-const:`telegram.constants.TopicLimit.MAX_NAME_LENGTH` characters. + icon_color (:obj:`int`): Color of the topic icon in RGB format. Currently, + must be one of :attr:`telegram.constants.ForumIconColor.BLUE`, + :attr:`telegram.constants.ForumIconColor.YELLOW`, + :attr:`telegram.constants.ForumIconColor.PURPLE`, + :attr:`telegram.constants.ForumIconColor.GREEN`, + :attr:`telegram.constants.ForumIconColor.PINK`, or + :attr:`telegram.constants.ForumIconColor.RED`. + icon_custom_emoji_id (:obj:`str`): New unique identifier of the custom emoji shown as + the topic icon. Use :meth:`~telegram.Bot.get_forum_topic_icon_stickers` to get all + allowed custom emoji identifiers. + + Returns: + :obj:`bool`: On success, :class:`telegram.ForumTopic` is returned. + + Raises: + :class:`telegram.error.TelegramError` + """ + data: JSONDict = { + "chat_id": chat_id, + "name": name, + "icon_color": icon_color, + "icon_custom_emoji_id": icon_custom_emoji_id, + } + # TODO: DO ForumTopic.de_json here! + return await self._post( # type: ignore[return-value] + "createForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + @_log + async def edit_forum_topic( + self, + chat_id: Union[str, int], + message_thread_id: int, + name: str, + icon_custom_emoji_id: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """ + Use this method to edit name and icon of a topic in a forum supergroup chat. The bot must + be an administrator in the chat for this to work and must have + :paramref:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights, + unless it is the creator of the topic. + + .. seealso:: :meth:`telegram.Message.edit_forum_topic`, + :meth:`telegram.Chat.edit_forum_topic`, + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + message_thread_id (:obj:`int`): |message_thread_id| + name (:obj:`str`): New topic name, + :tg-const:`telegram.constants.TopicLimit.MIN_NAME_LENGTH`- + :tg-const:`telegram.constants.TopicLimit.MAX_NAME_LENGTH` characters. + icon_custom_emoji_id (:obj:`str`): New unique identifier of the custom emoji shown as + the topic icon. Use :meth:`~telegram.Bot.get_forum_topic_icon_stickers` to get all + allowed custom emoji identifiers. + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_thread_id": message_thread_id, + "name": name, + "icon_custom_emoji_id": icon_custom_emoji_id, + } + return await self._post( # type: ignore[return-value] + "editForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + @_log + async def close_forum_topic( + self, + chat_id: Union[str, int], + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """ + Use this method to close an open topic in a forum supergroup chat. The bot must + be an administrator in the chat for this to work and must have + :paramref:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights, + unless it is the creator of the topic. + + .. seealso:: :meth:`telegram.Message.close_forum_topic`, + :meth:`telegram.Chat.close_forum_topic`, + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + message_thread_id (:obj:`int`): |message_thread_id| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_thread_id": message_thread_id, + } + return await self._post( # type: ignore[return-value] + "closeForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + @_log + async def reopen_forum_topic( + self, + chat_id: Union[str, int], + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """ + Use this method to reopen a closed topic in a forum supergroup chat. The bot must + be an administrator in the chat for this to work and must have + :paramref:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights, + unless it is the creator of the topic. + + .. seealso:: :meth:`telegram.Message.reopen_forum_topic`, + :meth:`telegram.Chat.reopen_forum_topic`, + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + message_thread_id (:obj:`int`): |message_thread_id| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_thread_id": message_thread_id, + } + return await self._post( # type: ignore[return-value] + "reopenForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + @_log + async def delete_forum_topic( + self, + chat_id: Union[str, int], + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """ + Use this method to delete a forum topic along with all its messages in a forum supergroup + chat. The bot must be an administrator in the chat for this to work and must have + :paramref:`~telegram.ChatAdministratorRights.can_delete_messages` administrator rights, + unless it is the creator of the topic. + + .. seealso:: :meth:`telegram.Message.delete_forum_topic`, + :meth:`telegram.Chat.delete_forum_topic`, + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + message_thread_id (:obj:`int`): |message_thread_id| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_thread_id": message_thread_id, + } + return await self._post( # type: ignore[return-value] + "deleteForumTopic", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + @_log + async def unpin_all_forum_topic_messages( + self, + chat_id: Union[str, int], + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """ + Use this method to clear the list of pinned messages in a forum topic. The bot must + be an administrator in the chat for this to work and must have + :paramref:`~telegram.ChatAdministratorRights.can_pin_messages` administrator rights + in the supergroup, unless it is the creator of the topic. + + .. seealso:: :meth:`telegram.Message.unpin_all_forum_topic_messages`, + :meth:`telegram.Chat.unpin_all_forum_topic_messages`, + + .. versionadded:: 20.0 + + Args: + chat_id (:obj:`int` | :obj:`str`): |chat_id_group| + message_thread_id (:obj:`int`): |message_thread_id| + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + + Raises: + :class:`telegram.error.TelegramError` + + """ + data: JSONDict = { + "chat_id": chat_id, + "message_thread_id": message_thread_id, + } + return await self._post( # type: ignore[return-value] + "unpinAllForumTopicMessages", + data, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + def to_dict(self, recursive: bool = True) -> JSONDict: # skipcq: PYL-W0613 """See :meth:`telegram.TelegramObject.to_dict`.""" data: JSONDict = {"id": self.id, "username": self.username, "first_name": self.first_name} @@ -7070,3 +7415,17 @@ def __hash__(self) -> int: """Alias for :meth:`set_my_default_administrator_rights`""" createInvoiceLink = create_invoice_link """Alias for :meth:`create_invoice_link`""" + getForumTopicIconStickers = get_forum_topic_icon_stickers + """Alias for :meth:`get_forum_topic_icon_stickers`""" + createForumTopic = create_forum_topic + """Alias for :meth:`create_forum_topic`""" + editForumTopic = edit_forum_topic + """Alias for :meth:`edit_forum_topic`""" + closeForumTopic = close_forum_topic + """Alias for :meth:`close_forum_topic`""" + reopenForumTopic = reopen_forum_topic + """Alias for :meth:`reopen_forum_topic`""" + deleteForumTopic = delete_forum_topic + """Alias for :meth:`delete_forum_topic`""" + unpinAllForumTopicMessages = unpin_all_forum_topic_messages + """Alias for :meth:`unpin_all_forum_topic_messages`""" diff --git a/telegram/_chat.py b/telegram/_chat.py index 85a51e0aece..91a751c53aa 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -2617,6 +2617,207 @@ async def set_menu_button( api_kwargs=api_kwargs, ) + async def create_forum_topic( + self, + name: str, + icon_color: int, + icon_custom_emoji_id: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """Shortcut for:: + + await bot.create_forum_topic(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.create_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().create_forum_topic( + chat_id=self.id, + name=name, + icon_color=icon_color, + icon_custom_emoji_id=icon_custom_emoji_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def edit_forum_topic( + self, + message_thread_id: int, + name: str, + icon_custom_emoji_id: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """Shortcut for:: + + await bot.edit_forum_topic(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().edit_forum_topic( + chat_id=self.id, + message_thread_id=message_thread_id, + name=name, + icon_custom_emoji_id=icon_custom_emoji_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def close_forum_topic( + self, + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """Shortcut for:: + + await bot.close_forum_topic(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.close_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().close_forum_topic( + chat_id=self.id, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def reopen_forum_topic( + self, + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """Shortcut for:: + + await bot.reopen_forum_topic(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.reopen_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().reopen_forum_topic( + chat_id=self.id, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_forum_topic( + self, + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """Shortcut for:: + + await bot.delete_forum_topic(chat_id=update.effective_chat.id, *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.delete_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().delete_forum_topic( + chat_id=self.id, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unpin_all_forum_topic_messages( + self, + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """Shortcut for:: + + await bot.unpin_all_forum_topic_messages(chat_id=update.effective_chat.id, + *args, **kwargs) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.unpin_all_forum_topic_messages`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().unpin_all_forum_topic_messages( + chat_id=self.id, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + async def get_menu_button( self, *, diff --git a/telegram/_message.py b/telegram/_message.py index e9d76b2ced4..f18e00973ea 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -2712,6 +2712,180 @@ async def unpin( api_kwargs=api_kwargs, ) + async def edit_forum_topic( + self, + name: str, + icon_custom_emoji_id: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """Shortcut for:: + + await bot.edit_forum_topic( + chat_id=message.chat_id, message_thread_id=message.message_thread_id, *args, + **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.edit_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().edit_forum_topic( + chat_id=self.chat_id, + message_thread_id=self.message_thread_id, + name=name, + icon_custom_emoji_id=icon_custom_emoji_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def close_forum_topic( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """Shortcut for:: + + await bot.close_forum_topic( + chat_id=message.chat_id, message_thread_id=message.message_thread_id, *args, + **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.close_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().close_forum_topic( + chat_id=self.chat_id, + message_thread_id=self.message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def reopen_forum_topic( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """Shortcut for:: + + await bot.reopen_forum_topic( + chat_id=message.chat_id, message_thread_id=message.message_thread_id, *args, + **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.reopen_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().reopen_forum_topic( + chat_id=self.chat_id, + message_thread_id=self.message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def delete_forum_topic( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """Shortcut for:: + + await bot.delete_forum_topic( + chat_id=message.chat_id, message_thread_id=message.message_thread_id, *args, + **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.delete_forum_topic`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().delete_forum_topic( + chat_id=self.chat_id, + message_thread_id=self.message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + + async def unpin_all_forum_topic_messages( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + ) -> bool: + """Shortcut for:: + + await bot.unpin_all_forum_topic_messages( + chat_id=message.chat_id, message_thread_id=message.message_thread_id, *args, + **kwargs + ) + + For the documentation of the arguments, please see + :meth:`telegram.Bot.unpin_all_forum_topic_messages`. + + .. versionadded:: 20.0 + + Returns: + :obj:`bool`: On success, :obj:`True` is returned. + """ + return await self.get_bot().unpin_all_forum_topic_messages( + chat_id=self.chat_id, + message_thread_id=self.message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=api_kwargs, + ) + def parse_entity(self, entity: MessageEntity) -> str: """Returns the text from a given :class:`telegram.MessageEntity`. diff --git a/telegram/constants.py b/telegram/constants.py index b20f847dc52..1a5bb3e9405 100644 --- a/telegram/constants.py +++ b/telegram/constants.py @@ -46,6 +46,7 @@ "DiceEmoji", "FileSizeLimit", "FloodLimit", + "ForumIconColor", "InlineKeyboardMarkupLimit", "InlineQueryLimit", "InlineQueryResultType", @@ -63,6 +64,7 @@ "PollType", "SUPPORTED_WEBHOOK_PORTS", "StickerType", + "TopicLimit", "WebhookLimit", "UpdateType", ] @@ -371,6 +373,66 @@ class FloodLimit(IntEnum): """ +class ForumIconColor(IntEnum): + """This enum contains the available colors for use in + :paramref:`telegram.Bot.create_forum_topic.icon_color`. The enum members of this enumeration + are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + BLUE = 0x6FB9F0 + """:obj:`int`: An icon with a color which corresponds to blue. + + .. raw:: html + +
+ + """ + YELLOW = 0xFFD67E + """:obj:`int`: An icon with a color which corresponds to yellow. + + .. raw:: html + +
+ + """ + PURPLE = 0xCB86DB + """:obj:`int`: An icon with a color which corresponds to purple. + + .. raw:: html + +
+ + """ + GREEN = 0x8EEE98 + """:obj:`int`: An icon with a color which corresponds to green. + + .. raw:: html + +
+ + """ + PINK = 0xFF93B2 + """:obj:`int`: An icon with a color which corresponds to pink. + + .. raw:: html + +
+ + """ + RED = 0xFB6F5F + """:obj:`int`: An icon with a color which corresponds to red. + + .. raw:: html + +
+ + """ + + class InlineKeyboardMarkupLimit(IntEnum): """This enum contains limitations for :class:`telegram.InlineKeyboardMarkup`/ :meth:`telegram.Bot.send_message` & friends. The enum @@ -917,3 +979,18 @@ class ChatLimit(IntEnum): """:obj:`int`: Minimum length of a new chat title.""" MAX_TITLE_LENGTH = 128 """:obj:`int`: Maximum length of a new chat title.""" + + +class TopicLimit(IntEnum): + """This enum contains limitations for :paramref:`telegram.Bot.edit_forum_topic.name`. The + enum members of this enumeration are instances of :class:`int` and can be treated as such. + + .. versionadded:: 20.0 + """ + + __slots__ = () + + MIN_NAME_LENGTH = 1 + """:obj:`int`: Minimum length of the topic name.""" + MAX_NAME_LENGTH = 128 + """:obj:`int`: Maximum length of the topic name.""" diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 7445c0bffd4..0ae8f88bcac 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -1107,6 +1107,28 @@ async def delete_chat_sticker_set( api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), ) + async def delete_forum_topic( + self, + chat_id: Union[str, int], + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + rate_limit_args: RLARGS = None, + ) -> bool: + return await super().delete_forum_topic( + chat_id=chat_id, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + ) + async def delete_message( self, chat_id: Union[str, int], @@ -1221,6 +1243,32 @@ async def edit_chat_invite_link( api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), ) + async def edit_forum_topic( + self, + chat_id: Union[str, int], + message_thread_id: int, + name: str, + icon_custom_emoji_id: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + rate_limit_args: RLARGS = None, + ) -> bool: + return await super().edit_forum_topic( + chat_id=chat_id, + message_thread_id=message_thread_id, + name=name, + icon_custom_emoji_id=icon_custom_emoji_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + ) + async def edit_message_caption( self, chat_id: Union[str, int] = None, @@ -1533,6 +1581,24 @@ async def get_file( api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), ) + async def get_forum_topic_icon_stickers( + self, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + rate_limit_args: RLARGS = None, + ) -> List[Sticker]: + return await super().get_forum_topic_icon_stickers( + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + ) + async def get_game_high_scores( self, user_id: Union[int, str], @@ -1757,6 +1823,54 @@ async def close( api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), ) + async def close_forum_topic( + self, + chat_id: Union[str, int], + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + rate_limit_args: RLARGS = None, + ) -> bool: + return await super().close_forum_topic( + chat_id=chat_id, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + ) + + async def create_forum_topic( + self, + chat_id: Union[str, int], + name: str, + icon_color: int, + icon_custom_emoji_id: str, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + rate_limit_args: RLARGS = None, + ) -> bool: + return await super().create_forum_topic( + chat_id=chat_id, + name=name, + icon_color=icon_color, + icon_custom_emoji_id=icon_custom_emoji_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + ) + async def pin_chat_message( self, chat_id: Union[str, int], @@ -1827,6 +1941,28 @@ async def promote_chat_member( api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), ) + async def reopen_forum_topic( + self, + chat_id: Union[str, int], + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + rate_limit_args: RLARGS = None, + ) -> bool: + return await super().reopen_forum_topic( + chat_id=chat_id, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + ) + async def restrict_chat_member( self, chat_id: Union[str, int], @@ -3121,6 +3257,28 @@ async def unpin_chat_message( api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), ) + async def unpin_all_forum_topic_messages( + self, + chat_id: Union[str, int], + message_thread_id: int, + *, + read_timeout: ODVInput[float] = DEFAULT_NONE, + write_timeout: ODVInput[float] = DEFAULT_NONE, + connect_timeout: ODVInput[float] = DEFAULT_NONE, + pool_timeout: ODVInput[float] = DEFAULT_NONE, + api_kwargs: JSONDict = None, + rate_limit_args: RLARGS = None, + ) -> bool: + return await super().unpin_all_forum_topic_messages( + chat_id=chat_id, + message_thread_id=message_thread_id, + read_timeout=read_timeout, + write_timeout=write_timeout, + connect_timeout=connect_timeout, + pool_timeout=pool_timeout, + api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args), + ) + async def upload_sticker_file( self, user_id: Union[str, int], @@ -3232,3 +3390,10 @@ async def upload_sticker_file( getMyDefaultAdministratorRights = get_my_default_administrator_rights setMyDefaultAdministratorRights = set_my_default_administrator_rights createInvoiceLink = create_invoice_link + getForumTopicIconStickers = get_forum_topic_icon_stickers + createForumTopic = create_forum_topic + editForumTopic = edit_forum_topic + closeForumTopic = close_forum_topic + reopenForumTopic = reopen_forum_topic + deleteForumTopic = delete_forum_topic + unpinAllForumTopicMessages = unpin_all_forum_topic_messages diff --git a/tests/test_bot.py b/tests/test_bot.py index 85f9c59444b..8a40bc086cd 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -2568,6 +2568,9 @@ async def test_pin_and_unpin_message(self, bot, super_group_id): # set_sticker_position_in_set, delete_sticker_from_set and get_custom_emoji_stickers # are tested in the test_sticker module. + # get_forum_topic_icon_stickers, edit_forum_topic, etc... + # are tested in the test_forum module. + async def test_timeout_propagation_explicit(self, monkeypatch, bot, chat_id): # Use BaseException that's not a subclass of Exception such that # OkException should not be caught anywhere diff --git a/tests/test_chat.py b/tests/test_chat.py index 4f2305180b8..e8871deeedd 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -896,6 +896,128 @@ async def make_assertion(*_, **kwargs): monkeypatch.setattr(chat.get_bot(), "decline_chat_join_request", make_assertion) assert await chat.decline_join_request(user_id=42) + async def test_create_forum_topic(self, monkeypatch, chat): + async def make_assertion(*_, **kwargs): + return ( + kwargs["chat_id"] == chat.id + and kwargs["name"] == "New Name" + and kwargs["icon_color"] == 0x6FB9F0 + and kwargs["icon_custom_emoji_id"] == "12345" + ) + + assert check_shortcut_signature( + Chat.create_forum_topic, Bot.create_forum_topic, ["chat_id"], [] + ) + assert await check_shortcut_call( + chat.create_forum_topic, + chat.get_bot(), + "create_forum_topic", + shortcut_kwargs=["chat_id"], + ) + assert await check_defaults_handling(chat.create_forum_topic, chat.get_bot()) + + monkeypatch.setattr(chat.get_bot(), "create_forum_topic", make_assertion) + assert await chat.create_forum_topic( + name="New Name", icon_color=0x6FB9F0, icon_custom_emoji_id="12345" + ) + + async def test_edit_forum_topic(self, monkeypatch, chat): + async def make_assertion(*_, **kwargs): + return ( + kwargs["chat_id"] == chat.id + and kwargs["message_thread_id"] == 42 + and kwargs["name"] == "New Name" + and kwargs["icon_custom_emoji_id"] == "12345" + ) + + assert check_shortcut_signature( + Chat.edit_forum_topic, Bot.edit_forum_topic, ["chat_id"], [] + ) + assert await check_shortcut_call( + chat.edit_forum_topic, chat.get_bot(), "edit_forum_topic", shortcut_kwargs=["chat_id"] + ) + assert await check_defaults_handling(chat.edit_forum_topic, chat.get_bot()) + + monkeypatch.setattr(chat.get_bot(), "edit_forum_topic", make_assertion) + assert await chat.edit_forum_topic( + message_thread_id=42, name="New Name", icon_custom_emoji_id="12345" + ) + + async def test_close_forum_topic(self, monkeypatch, chat): + async def make_assertion(*_, **kwargs): + return kwargs["chat_id"] == chat.id and kwargs["message_thread_id"] == 42 + + assert check_shortcut_signature( + Chat.close_forum_topic, Bot.close_forum_topic, ["chat_id"], [] + ) + assert await check_shortcut_call( + chat.close_forum_topic, + chat.get_bot(), + "close_forum_topic", + shortcut_kwargs=["chat_id"], + ) + assert await check_defaults_handling(chat.close_forum_topic, chat.get_bot()) + + monkeypatch.setattr(chat.get_bot(), "close_forum_topic", make_assertion) + assert await chat.close_forum_topic(message_thread_id=42) + + async def test_reopen_forum_topic(self, monkeypatch, chat): + async def make_assertion(*_, **kwargs): + return kwargs["chat_id"] == chat.id and kwargs["message_thread_id"] == 42 + + assert check_shortcut_signature( + Chat.reopen_forum_topic, Bot.reopen_forum_topic, ["chat_id"], [] + ) + assert await check_shortcut_call( + chat.reopen_forum_topic, + chat.get_bot(), + "reopen_forum_topic", + shortcut_kwargs=["chat_id"], + ) + assert await check_defaults_handling(chat.reopen_forum_topic, chat.get_bot()) + + monkeypatch.setattr(chat.get_bot(), "reopen_forum_topic", make_assertion) + assert await chat.reopen_forum_topic(message_thread_id=42) + + async def test_delete_forum_topic(self, monkeypatch, chat): + async def make_assertion(*_, **kwargs): + return kwargs["chat_id"] == chat.id and kwargs["message_thread_id"] == 42 + + assert check_shortcut_signature( + Chat.delete_forum_topic, Bot.delete_forum_topic, ["chat_id"], [] + ) + assert await check_shortcut_call( + chat.delete_forum_topic, + chat.get_bot(), + "delete_forum_topic", + shortcut_kwargs=["chat_id"], + ) + assert await check_defaults_handling(chat.delete_forum_topic, chat.get_bot()) + + monkeypatch.setattr(chat.get_bot(), "delete_forum_topic", make_assertion) + assert await chat.delete_forum_topic(message_thread_id=42) + + async def test_unpin_all_forum_topic_messages(self, monkeypatch, chat): + async def make_assertion(*_, **kwargs): + return kwargs["chat_id"] == chat.id and kwargs["message_thread_id"] == 42 + + assert check_shortcut_signature( + Chat.unpin_all_forum_topic_messages, + Bot.unpin_all_forum_topic_messages, + ["chat_id"], + [], + ) + assert await check_shortcut_call( + chat.unpin_all_forum_topic_messages, + chat.get_bot(), + "unpin_all_forum_topic_messages", + shortcut_kwargs=["chat_id"], + ) + assert await check_defaults_handling(chat.unpin_all_forum_topic_messages, chat.get_bot()) + + monkeypatch.setattr(chat.get_bot(), "unpin_all_forum_topic_messages", make_assertion) + assert await chat.unpin_all_forum_topic_messages(message_thread_id=42) + def test_mention_html(self): with pytest.raises(TypeError, match="Can not create a mention to a private group chat"): chat = Chat(id=1, type="foo") diff --git a/tests/test_message.py b/tests/test_message.py index 3ad03e386cc..bb8aa8519c6 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -198,6 +198,7 @@ def message(bot): ] }, {"web_app_data": WebAppData("some_data", "some_button_text")}, + {"message_thread_id": 123}, ], ids=[ "forwarded_user", @@ -251,6 +252,7 @@ def message(bot): "has_protected_content", "entities", "web_app_data", + "message_thread_id", ], ) def message_params(bot, request): @@ -1693,6 +1695,124 @@ def test_default_quote(self, message): finally: message.get_bot()._defaults = None + async def test_edit_forum_topic(self, monkeypatch, message): + async def make_assertion(*_, **kwargs): + return ( + kwargs["chat_id"] == message.chat_id + and kwargs["message_thread_id"] == message.message_thread_id + and kwargs["name"] == "New Name" + and kwargs["icon_custom_emoji_id"] == "12345" + ) + + assert check_shortcut_signature( + Message.edit_forum_topic, Bot.edit_forum_topic, ["chat_id", "message_thread_id"], [] + ) + assert await check_shortcut_call( + message.edit_forum_topic, + message.get_bot(), + "edit_forum_topic", + shortcut_kwargs=["chat_id", "message_thread_id"], + ) + assert await check_defaults_handling(message.edit_forum_topic, message.get_bot()) + + monkeypatch.setattr(message.get_bot(), "edit_forum_topic", make_assertion) + assert await message.edit_forum_topic(name="New Name", icon_custom_emoji_id="12345") + + async def test_close_forum_topic(self, monkeypatch, message): + async def make_assertion(*_, **kwargs): + return ( + kwargs["chat_id"] == message.chat_id + and kwargs["message_thread_id"] == message.message_thread_id + ) + + assert check_shortcut_signature( + Message.close_forum_topic, Bot.close_forum_topic, ["chat_id", "message_thread_id"], [] + ) + assert await check_shortcut_call( + message.close_forum_topic, + message.get_bot(), + "close_forum_topic", + shortcut_kwargs=["chat_id", "message_thread_id"], + ) + assert await check_defaults_handling(message.close_forum_topic, message.get_bot()) + + monkeypatch.setattr(message.get_bot(), "close_forum_topic", make_assertion) + assert await message.close_forum_topic() + + async def test_reopen_forum_topic(self, monkeypatch, message): + async def make_assertion(*_, **kwargs): + return ( + kwargs["chat_id"] == message.chat_id + and kwargs["message_thread_id"] == message.message_thread_id + ) + + assert check_shortcut_signature( + Message.reopen_forum_topic, + Bot.reopen_forum_topic, + ["chat_id", "message_thread_id"], + [], + ) + assert await check_shortcut_call( + message.reopen_forum_topic, + message.get_bot(), + "reopen_forum_topic", + shortcut_kwargs=["chat_id", "message_thread_id"], + ) + assert await check_defaults_handling(message.reopen_forum_topic, message.get_bot()) + + monkeypatch.setattr(message.get_bot(), "reopen_forum_topic", make_assertion) + assert await message.reopen_forum_topic() + + async def test_delete_forum_topic(self, monkeypatch, message): + async def make_assertion(*_, **kwargs): + return ( + kwargs["chat_id"] == message.chat_id + and kwargs["message_thread_id"] == message.message_thread_id + ) + + assert check_shortcut_signature( + Message.delete_forum_topic, + Bot.delete_forum_topic, + ["chat_id", "message_thread_id"], + [], + ) + assert await check_shortcut_call( + message.delete_forum_topic, + message.get_bot(), + "delete_forum_topic", + shortcut_kwargs=["chat_id", "message_thread_id"], + ) + assert await check_defaults_handling(message.delete_forum_topic, message.get_bot()) + + monkeypatch.setattr(message.get_bot(), "delete_forum_topic", make_assertion) + assert await message.delete_forum_topic() + + async def test_unpin_all_forum_topic_messages(self, monkeypatch, message): + async def make_assertion(*_, **kwargs): + return ( + kwargs["chat_id"] == message.chat_id + and kwargs["message_thread_id"] == message.message_thread_id + ) + + assert check_shortcut_signature( + Message.unpin_all_forum_topic_messages, + Bot.unpin_all_forum_topic_messages, + ["chat_id", "message_thread_id"], + [], + ) + assert await check_shortcut_call( + message.unpin_all_forum_topic_messages, + message.get_bot(), + "unpin_all_forum_topic_messages", + shortcut_kwargs=["chat_id", "message_thread_id"], + ) + assert await check_defaults_handling( + message.unpin_all_forum_topic_messages, message.get_bot() + ) + + monkeypatch.setattr(message.get_bot(), "unpin_all_forum_topic_messages", make_assertion) + assert await message.unpin_all_forum_topic_messages() + def test_equality(self): id_ = 1 a = Message(