Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
This commit is contained in:
Poolitzer 2022-12-06 10:13:06 +01:00 committed by GitHub
parent 01167c805b
commit d6c6cc231e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 1956 additions and 29 deletions

View file

@ -14,7 +14,7 @@ repos:
args: args:
- --diff - --diff
- --check - --check
- repo: https://gitlab.com/pycqa/flake8 - repo: https://github.com/PyCQA/flake8
rev: 3.9.2 rev: 3.9.2
hooks: hooks:
- id: flake8 - id: flake8

View file

@ -20,7 +20,7 @@ We have a vibrant community of developers helping each other in our `Telegram gr
:target: https://pypi.org/project/python-telegram-bot/ :target: https://pypi.org/project/python-telegram-bot/
:alt: Supported Python versions :alt: Supported Python versions
.. image:: https://img.shields.io/badge/Bot%20API-6.2-blue?logo=telegram .. image:: https://img.shields.io/badge/Bot%20API-6.3-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog :target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API versions :alt: Supported Bot API versions

View file

@ -20,7 +20,7 @@ We have a vibrant community of developers helping each other in our `Telegram gr
:target: https://pypi.org/project/python-telegram-bot-raw/ :target: https://pypi.org/project/python-telegram-bot-raw/
:alt: Supported Python versions :alt: Supported Python versions
.. image:: https://img.shields.io/badge/Bot%20API-6.2-blue?logo=telegram .. image:: https://img.shields.io/badge/Bot%20API-6.3-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog :target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API versions :alt: Supported Bot API versions

View file

@ -13,6 +13,7 @@
# serve to show the default. # serve to show the default.
import sys import sys
import os import os
from pathlib import Path
# import telegram # import telegram
# If extensions (or modules to document with autodoc) are in another directory, # If extensions (or modules to document with autodoc) are in another directory,
@ -50,6 +51,9 @@ source_suffix = '.rst'
# The master toctree document. # The master toctree document.
master_doc = 'index' master_doc = 'index'
# Global substitutions
rst_prolog = (Path.cwd() / "substitutions/global.rst").read_text(encoding="utf-8")
# General information about the project. # General information about the project.
project = u'python-telegram-bot' project = u'python-telegram-bot'
copyright = u'2015-2022, Leandro Toledo' copyright = u'2015-2022, Leandro Toledo'

View file

@ -0,0 +1,5 @@
.. |message_thread_id| replace:: Unique identifier for the target message thread of the forum topic.
.. |message_thread_id_arg| replace:: Unique identifier for the target message thread (topic) of the forum; for forum supergroups only.
.. |chat_id_group| replace:: Unique identifier for the target chat or username of the target supergroup (in the format ``@supergroupusername``).

View file

@ -0,0 +1,8 @@
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/telegram/forumtopic.py
telegram.ForumTopic
===================
.. autoclass:: telegram.ForumTopic
:members:
:show-inheritance:

View file

@ -0,0 +1,8 @@
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/telegram/forumtopic.py
telegram.ForumTopicClosed
=========================
.. autoclass:: telegram.ForumTopicClosed
:members:
:show-inheritance:

View file

@ -0,0 +1,8 @@
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/telegram/forumtopic.py
telegram.ForumTopicCreated
==========================
.. autoclass:: telegram.ForumTopicCreated
:members:
:show-inheritance:

View file

@ -0,0 +1,8 @@
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/telegram/forumtopic.py
telegram.ForumTopicReopened
===========================
.. autoclass:: telegram.ForumTopicReopened
:members:
:show-inheritance:

View file

@ -39,6 +39,10 @@ telegram package
telegram.error telegram.error
telegram.file telegram.file
telegram.forcereply telegram.forcereply
telegram.forumtopic
telegram.forumtopicclosed
telegram.forumtopiccreated
telegram.forumtopicreopened
telegram.inlinekeyboardbutton telegram.inlinekeyboardbutton
telegram.inlinekeyboardmarkup telegram.inlinekeyboardmarkup
telegram.inputfile telegram.inputfile

View file

@ -64,6 +64,7 @@ from .replymarkup import ReplyMarkup
from .replykeyboardmarkup import ReplyKeyboardMarkup from .replykeyboardmarkup import ReplyKeyboardMarkup
from .replykeyboardremove import ReplyKeyboardRemove from .replykeyboardremove import ReplyKeyboardRemove
from .forcereply import ForceReply from .forcereply import ForceReply
from .forumtopic import ForumTopic, ForumTopicClosed, ForumTopicCreated, ForumTopicReopened
from .error import TelegramError from .error import TelegramError
from .files.inputfile import InputFile from .files.inputfile import InputFile
from .files.file import File from .files.file import File
@ -230,6 +231,10 @@ __all__ = ( # Keep this alphabetically ordered
'File', 'File',
'FileCredentials', 'FileCredentials',
'ForceReply', 'ForceReply',
'ForumTopic',
'ForumTopicClosed',
'ForumTopicCreated',
'ForumTopicReopened',
'Game', 'Game',
'GameHighScore', 'GameHighScore',
'IdDocumentData', 'IdDocumentData',

View file

@ -94,6 +94,7 @@ from telegram import (
) )
from telegram.constants import MAX_INLINE_QUERY_RESULTS from telegram.constants import MAX_INLINE_QUERY_RESULTS
from telegram.error import InvalidToken, TelegramError from telegram.error import InvalidToken, TelegramError
from telegram.forumtopic import ForumTopic
from telegram.utils.deprecate import TelegramDeprecationWarning from telegram.utils.deprecate import TelegramDeprecationWarning
from telegram.utils.helpers import ( from telegram.utils.helpers import (
DEFAULT_NONE, DEFAULT_NONE,
@ -310,6 +311,7 @@ class Bot(TelegramObject):
timeout: ODVInput[float] = DEFAULT_NONE, timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Union[bool, Message]: ) -> Union[bool, Message]:
if reply_to_message_id is not None: if reply_to_message_id is not None:
data['reply_to_message_id'] = reply_to_message_id data['reply_to_message_id'] = reply_to_message_id
@ -317,6 +319,9 @@ class Bot(TelegramObject):
if protect_content: if protect_content:
data['protect_content'] = protect_content data['protect_content'] = protect_content
if message_thread_id is not None:
data["message_thread_id"] = message_thread_id
# We don't check if (DEFAULT_)None here, so that _put is able to insert the defaults # We don't check if (DEFAULT_)None here, so that _put is able to insert the defaults
# correctly, if necessary # correctly, if necessary
data['disable_notification'] = disable_notification data['disable_notification'] = disable_notification
@ -471,6 +476,7 @@ class Bot(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
"""Use this method to send text messages. """Use this method to send text messages.
@ -492,6 +498,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message
@ -532,6 +541,7 @@ class Bot(TelegramObject):
timeout=timeout, timeout=timeout,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -547,6 +557,8 @@ class Bot(TelegramObject):
limitations: limitations:
- A message can only be deleted if it was sent less than 48 hours ago. - A message can only be deleted if it was sent less than 48 hours ago.
- Service messages about a supergroup, channel, or forum topic creation can't be
deleted.
- A dice message in a private chat can only be deleted if it was sent more than 24 - A dice message in a private chat can only be deleted if it was sent more than 24
hours ago. hours ago.
- Bots can delete outgoing messages in private chats, groups, and supergroups. - Bots can delete outgoing messages in private chats, groups, and supergroups.
@ -590,6 +602,7 @@ class Bot(TelegramObject):
timeout: ODVInput[float] = DEFAULT_NONE, timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
"""Use this method to forward messages of any kind. Service messages can't be forwarded. """Use this method to forward messages of any kind. Service messages can't be forwarded.
@ -613,6 +626,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as 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 read timeout from the server (instead of the one specified during creation of
@ -642,6 +658,7 @@ class Bot(TelegramObject):
timeout=timeout, timeout=timeout,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -660,6 +677,7 @@ class Bot(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
"""Use this method to send photos. """Use this method to send photos.
@ -698,6 +716,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -739,6 +760,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -761,6 +783,7 @@ class Bot(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
""" """
Use this method to send audio files, if you want Telegram clients to display them in the Use this method to send audio files, if you want Telegram clients to display them in the
@ -809,6 +832,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -867,6 +893,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -887,6 +914,7 @@ class Bot(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
""" """
Use this method to send general files. Use this method to send general files.
@ -929,6 +957,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -983,6 +1014,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -997,6 +1029,7 @@ class Bot(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
""" """
Use this method to send static ``.WEBP``, animated ``.TGS``, or video ``.WEBM`` stickers. Use this method to send static ``.WEBP``, animated ``.TGS``, or video ``.WEBM`` stickers.
@ -1023,6 +1056,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -1054,6 +1090,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -1077,6 +1114,7 @@ class Bot(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
""" """
Use this method to send video files, Telegram clients support mp4 videos Use this method to send video files, Telegram clients support mp4 videos
@ -1128,6 +1166,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -1187,6 +1228,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -1205,6 +1247,7 @@ class Bot(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
""" """
As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long.
@ -1243,6 +1286,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -1293,6 +1339,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -1315,6 +1362,7 @@ class Bot(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
""" """
Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound).
@ -1323,7 +1371,7 @@ class Bot(TelegramObject):
Note: Note:
``thumb`` will be ignored for small files, for which Telegram can easily ``thumb`` will be ignored for small files, for which Telegram can easily
generate thumb nails. However, this behaviour is undocumented and might be changed generate thumbnails. However, this behaviour is undocumented and might be changed
by Telegram. by Telegram.
Args: Args:
@ -1369,6 +1417,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -1417,6 +1468,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -1436,6 +1488,7 @@ class Bot(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
""" """
Use this method to send audio files, if you want Telegram clients to display the file Use this method to send audio files, if you want Telegram clients to display the file
@ -1479,6 +1532,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -1522,6 +1578,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -1537,6 +1594,7 @@ class Bot(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> List[Message]: ) -> List[Message]:
"""Use this method to send a group of photos or videos as an album. """Use this method to send a group of photos or videos as an album.
@ -1552,6 +1610,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -1587,6 +1648,9 @@ class Bot(TelegramObject):
if protect_content: if protect_content:
data['protect_content'] = protect_content data['protect_content'] = protect_content
if message_thread_id:
data["message_thread_id"] = message_thread_id
result = self._post('sendMediaGroup', data, timeout=timeout, api_kwargs=api_kwargs) result = self._post('sendMediaGroup', data, timeout=timeout, api_kwargs=api_kwargs)
return Message.de_list(result, self) # type: ignore return Message.de_list(result, self) # type: ignore
@ -1609,6 +1673,7 @@ class Bot(TelegramObject):
proximity_alert_radius: int = None, proximity_alert_radius: int = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
"""Use this method to send point on the map. """Use this method to send point on the map.
@ -1636,6 +1701,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -1692,6 +1760,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -1853,6 +1922,7 @@ class Bot(TelegramObject):
google_place_type: str = None, google_place_type: str = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
"""Use this method to send information about a venue. """Use this method to send information about a venue.
@ -1886,6 +1956,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -1950,6 +2023,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -1968,6 +2042,7 @@ class Bot(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
"""Use this method to send phone contacts. """Use this method to send phone contacts.
@ -1990,6 +2065,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -2043,6 +2121,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -2057,6 +2136,7 @@ class Bot(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
"""Use this method to send a game. """Use this method to send a game.
@ -2070,6 +2150,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -2103,6 +2186,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -2246,9 +2330,8 @@ class Bot(TelegramObject):
current_offset: str = None, current_offset: str = None,
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
) -> bool: ) -> bool:
""" """Use this method to send answers to an inline query. No more than 50 results per query
Use this method to send answers to an inline query. No more than 50 results per query are are allowed.
allowed.
Warning: Warning:
In most use cases :attr:`current_offset` should not be passed manually. Instead of In most use cases :attr:`current_offset` should not be passed manually. Instead of
@ -3627,6 +3710,7 @@ class Bot(TelegramObject):
max_tip_amount: int = None, max_tip_amount: int = None,
suggested_tip_amounts: List[int] = None, suggested_tip_amounts: List[int] = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
"""Use this method to send invoices. """Use this method to send invoices.
@ -3705,6 +3789,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -3779,6 +3866,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -4031,6 +4119,7 @@ class Bot(TelegramObject):
can_manage_chat: bool = None, can_manage_chat: bool = None,
can_manage_voice_chats: bool = None, can_manage_voice_chats: bool = None,
can_manage_video_chats: bool = None, can_manage_video_chats: bool = None,
can_manage_topics: bool = None,
) -> bool: ) -> bool:
""" """
Use this method to promote or demote a user in a supergroup or a channel. The bot must be Use this method to promote or demote a user in a supergroup or a channel. The bot must be
@ -4086,6 +4175,10 @@ class Bot(TelegramObject):
the connection pool). the connection pool).
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
Telegram API. Telegram API.
can_manage_topics (:obj:`bool`, optional): Pass :obj:`True`, if the user is
allowed to create, rename, close, and reopen forum topics; supergroups only.
.. versionadded:: 13.15
Returns: Returns:
:obj:`bool`: On success, :obj:`True` is returned. :obj:`bool`: On success, :obj:`True` is returned.
@ -4125,6 +4218,8 @@ class Bot(TelegramObject):
data['can_manage_video_chats'] = can_manage_voice_chats data['can_manage_video_chats'] = can_manage_voice_chats
if can_manage_video_chats is not None: if can_manage_video_chats is not None:
data['can_manage_video_chats'] = can_manage_video_chats data['can_manage_video_chats'] = can_manage_video_chats
if can_manage_topics is not None:
data["can_manage_topics"] = can_manage_topics
result = self._post('promoteChatMember', data, timeout=timeout, api_kwargs=api_kwargs) result = self._post('promoteChatMember', data, timeout=timeout, api_kwargs=api_kwargs)
@ -5273,6 +5368,7 @@ class Bot(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
""" """
Use this method to send a native poll. Use this method to send a native poll.
@ -5315,6 +5411,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -5376,6 +5475,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -5436,6 +5536,7 @@ class Bot(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Message: ) -> Message:
""" """
Use this method to send an animated emoji that will display a random value. Use this method to send an animated emoji that will display a random value.
@ -5456,6 +5557,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -5492,6 +5596,7 @@ class Bot(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
@log @log
@ -5813,6 +5918,7 @@ class Bot(TelegramObject):
timeout: ODVInput[float] = DEFAULT_NONE, timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> MessageId: ) -> MessageId:
""" """
Use this method to copy messages of any kind. Service messages and invoice messages can't Use this method to copy messages of any kind. Service messages and invoice messages can't
@ -5838,6 +5944,9 @@ class Bot(TelegramObject):
forwarding and saving. forwarding and saving.
.. versionadded:: 13.10 .. versionadded:: 13.10
message_thread_id (:obj:`int`, optional): |message_thread_id_arg|
.. versionadded:: 13.15
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
original message. original message.
@ -5881,6 +5990,8 @@ class Bot(TelegramObject):
data['reply_markup'] = reply_markup.to_json() data['reply_markup'] = reply_markup.to_json()
else: else:
data['reply_markup'] = reply_markup data['reply_markup'] = reply_markup
if message_thread_id:
data["message_thread_id"] = message_thread_id
result = self._post('copyMessage', data, timeout=timeout, api_kwargs=api_kwargs) result = self._post('copyMessage', data, timeout=timeout, api_kwargs=api_kwargs)
return MessageId.de_json(result, self) # type: ignore[return-value, arg-type] return MessageId.de_json(result, self) # type: ignore[return-value, arg-type]
@ -6096,6 +6207,338 @@ class Bot(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
) )
@log
def get_forum_topic_icon_stickers(
self,
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:: 13.15
Args:
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).
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
Telegram API.
Returns:
List[:class:`telegram.Sticker`]
Raises:
:class:`telegram.error.TelegramError`
"""
result = self._post(
"getForumTopicIconStickers",
timeout=timeout,
api_kwargs=api_kwargs,
)
return Sticker.de_list(result, self) # type: ignore[return-value, arg-type]
@log
def create_forum_topic(
self,
chat_id: Union[str, int],
name: str,
icon_color: int = None,
icon_custom_emoji_id: str = None,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> ForumTopic:
"""
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
:attr:`~telegram.ChatAdministratorRights.can_manage_topics` administrator rights.
.. seealso:: :meth:`telegram.Chat.create_forum_topic`,
.. versionadded:: 13.15
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_group|
name (:obj:`str`): New topic name, 1-128 characters.
icon_color (:obj:`int`, optional): Color of the topic icon in RGB format. Currently,
must be one of 7322096 (0x6FB9F0), 16766590 (0xFFD67E), 13338331 (0xCB86DB),
9367192 (0x8EEE98), 16749490 (0xFF93B2), or 16478047 (0xFB6F5F)
icon_custom_emoji_id (:obj:`str`, optional): 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.
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).
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
Telegram API.
Returns:
:class:`telegram.ForumTopic`
Raises:
:class:`telegram.error.TelegramError`
"""
data: JSONDict = {
"chat_id": chat_id,
"name": name,
}
if icon_color is not None:
data["icon_color"] = icon_color
if icon_custom_emoji_id is not None:
data["icon_custom_emoji_id"] = icon_custom_emoji_id
result = self._post(
"createForumTopic",
data,
timeout=timeout,
api_kwargs=api_kwargs,
)
return ForumTopic.de_json(result, self) # type: ignore[return-value, arg-type]
@log
def edit_forum_topic(
self,
chat_id: Union[str, int],
message_thread_id: int,
name: str,
icon_custom_emoji_id: str,
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
:attr:`~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:: 13.15
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_group|
message_thread_id (:obj:`int`): |message_thread_id|
.. versionadded:: 13.15
name (:obj:`str`): New topic name, 1-128 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.
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).
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
Telegram API.
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 self._post( # type: ignore[return-value]
"editForumTopic",
data,
timeout=timeout,
api_kwargs=api_kwargs,
)
@log
def close_forum_topic(
self,
chat_id: Union[str, int],
message_thread_id: int,
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
:attr:`~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:: 13.15
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_group|
message_thread_id (:obj:`int`): |message_thread_id|
.. versionadded:: 13.15
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).
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
Telegram API.
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 self._post( # type: ignore[return-value]
"closeForumTopic",
data,
timeout=timeout,
api_kwargs=api_kwargs,
)
@log
def reopen_forum_topic(
self,
chat_id: Union[str, int],
message_thread_id: int,
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
:meth:`~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:: 13.15
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_group|
message_thread_id (:obj:`int`): |message_thread_id|
.. versionadded:: 13.15
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).
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
Telegram API.
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 self._post( # type: ignore[return-value]
"reopenForumTopic",
data,
timeout=timeout,
api_kwargs=api_kwargs,
)
@log
def delete_forum_topic(
self,
chat_id: Union[str, int],
message_thread_id: int,
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
:meth:`~telegram.ChatAdministratorRights.can_delete_messages` administrator rights.
.. seealso:: :meth:`telegram.Message.delete_forum_topic`,
:meth:`telegram.Chat.delete_forum_topic`,
.. versionadded:: 13.15
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_group|
message_thread_id (:obj:`int`): |message_thread_id|
.. versionadded:: 13.15
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).
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
Telegram API.
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 self._post( # type: ignore[return-value]
"deleteForumTopic",
data,
timeout=timeout,
api_kwargs=api_kwargs,
)
@log
def unpin_all_forum_topic_messages(
self,
chat_id: Union[str, int],
message_thread_id: int,
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
:meth:`~telegram.ChatAdministratorRights.can_pin_messages` administrator rights
in the supergroup.
.. seealso:: :meth:`telegram.Message.unpin_all_forum_topic_messages`,
:meth:`telegram.Chat.unpin_all_forum_topic_messages`,
.. versionadded:: 13.15
Args:
chat_id (:obj:`int` | :obj:`str`): |chat_id_group|
message_thread_id (:obj:`int`): |message_thread_id|
.. versionadded:: 13.15
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).
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
Telegram API.
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 self._post( # type: ignore[return-value]
"unpinAllForumTopicMessages",
data,
timeout=timeout,
api_kwargs=api_kwargs,
)
def to_dict(self) -> JSONDict: def to_dict(self) -> JSONDict:
"""See :meth:`telegram.TelegramObject.to_dict`.""" """See :meth:`telegram.TelegramObject.to_dict`."""
data: JSONDict = {'id': self.id, 'username': self.username, 'first_name': self.first_name} data: JSONDict = {'id': self.id, 'username': self.username, 'first_name': self.first_name}
@ -6292,3 +6735,17 @@ class Bot(TelegramObject):
"""Alias for :meth:`set_my_default_administrator_rights`""" """Alias for :meth:`set_my_default_administrator_rights`"""
createInvoiceLink = create_invoice_link createInvoiceLink = create_invoice_link
"""Alias for :meth:`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`"""

View file

@ -621,6 +621,7 @@ class CallbackQuery(TelegramObject):
timeout: ODVInput[float] = DEFAULT_NONE, timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'MessageId': ) -> 'MessageId':
"""Shortcut for:: """Shortcut for::
@ -650,6 +651,7 @@ class CallbackQuery(TelegramObject):
timeout=timeout, timeout=timeout,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
MAX_ANSWER_TEXT_LENGTH: ClassVar[int] = constants.MAX_ANSWER_CALLBACK_QUERY_TEXT_LENGTH MAX_ANSWER_TEXT_LENGTH: ClassVar[int] = constants.MAX_ANSWER_CALLBACK_QUERY_TEXT_LENGTH

View file

@ -35,6 +35,7 @@ if TYPE_CHECKING:
Bot, Bot,
ChatMember, ChatMember,
ChatInviteLink, ChatInviteLink,
ForumTopic,
Message, Message,
MessageId, MessageId,
ReplyMarkup, ReplyMarkup,
@ -131,6 +132,21 @@ class Chat(TelegramObject):
in the private chat. Returned only in :meth:`telegram.Bot.get_chat`. in the private chat. Returned only in :meth:`telegram.Bot.get_chat`.
.. versionadded:: 13.14 .. versionadded:: 13.14
is_forum (:obj:`bool`, optional): :obj:`True`, if the supergroup chat is a forum
(has topics_ enabled).
.. versionadded:: 13.15
active_usernames (List[:obj:`str`], optional): If set, the list of all `active chat
usernames <https://telegram.org/blog/topics-in-groups-collectible-usernames\
#collectible-usernames>`_; for private chats, supergroups and channels. Returned
only in :meth:`telegram.Bot.get_chat`.
.. versionadded:: 13.15
emoji_status_custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of emoji
status of the other party in a private chat. Returned only in
:meth:`telegram.Bot.get_chat`.
.. versionadded:: 13.15
**kwargs (:obj:`dict`): Arbitrary keyword arguments. **kwargs (:obj:`dict`): Arbitrary keyword arguments.
Attributes: Attributes:
@ -190,6 +206,23 @@ class Chat(TelegramObject):
in the private chat. Returned only in :meth:`telegram.Bot.get_chat`. in the private chat. Returned only in :meth:`telegram.Bot.get_chat`.
.. versionadded:: 13.14 .. versionadded:: 13.14
is_forum (:obj:`bool`): Optional. :obj:`True`, if the supergroup chat is a forum
(has topics_ enabled).
.. versionadded:: 13.15
active_usernames (List[:obj:`str`]): Optional. If set, the list of all `active chat
usernames <https://telegram.org/blog/topics-in-groups-collectible-usernames\
#collectible-usernames>`_; for private chats, supergroups and channels. Returned
only in :meth:`telegram.Bot.get_chat`.
.. versionadded:: 13.15
emoji_status_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of emoji
status of the other party in a private chat. Returned only in
:meth:`telegram.Bot.get_chat`.
.. versionadded:: 13.15
.. _topics: https://telegram.org/blog/topics-in-groups-collectible-usernames#topics-in-groups
""" """
__slots__ = ( __slots__ = (
@ -218,6 +251,9 @@ class Chat(TelegramObject):
'join_to_send_messages', 'join_to_send_messages',
'join_by_request', 'join_by_request',
'has_restricted_voice_and_video_messages', 'has_restricted_voice_and_video_messages',
'is_forum',
'active_usernames',
'emoji_status_custom_emoji_id',
'_id_attrs', '_id_attrs',
) )
@ -261,6 +297,9 @@ class Chat(TelegramObject):
join_to_send_messages: bool = None, join_to_send_messages: bool = None,
join_by_request: bool = None, join_by_request: bool = None,
has_restricted_voice_and_video_messages: bool = None, has_restricted_voice_and_video_messages: bool = None,
is_forum: bool = None,
active_usernames: List[str] = None,
emoji_status_custom_emoji_id: str = None,
**_kwargs: Any, **_kwargs: Any,
): ):
# Required # Required
@ -292,6 +331,9 @@ class Chat(TelegramObject):
self.join_to_send_messages = join_to_send_messages self.join_to_send_messages = join_to_send_messages
self.join_by_request = join_by_request self.join_by_request = join_by_request
self.has_restricted_voice_and_video_messages = has_restricted_voice_and_video_messages self.has_restricted_voice_and_video_messages = has_restricted_voice_and_video_messages
self.is_forum = is_forum
self.active_usernames = active_usernames
self.emoji_status_custom_emoji_id = emoji_status_custom_emoji_id
self.bot = bot self.bot = bot
self._id_attrs = (self.id,) self._id_attrs = (self.id,)
@ -629,6 +671,7 @@ class Chat(TelegramObject):
can_manage_chat: bool = None, can_manage_chat: bool = None,
can_manage_voice_chats: bool = None, can_manage_voice_chats: bool = None,
can_manage_video_chats: bool = None, can_manage_video_chats: bool = None,
can_manage_topics: bool = None,
) -> bool: ) -> bool:
"""Shortcut for:: """Shortcut for::
@ -663,6 +706,7 @@ class Chat(TelegramObject):
can_manage_chat=can_manage_chat, can_manage_chat=can_manage_chat,
can_manage_voice_chats=can_manage_voice_chats, can_manage_voice_chats=can_manage_voice_chats,
can_manage_video_chats=can_manage_video_chats, can_manage_video_chats=can_manage_video_chats,
can_manage_topics=can_manage_topics,
) )
def restrict_member( def restrict_member(
@ -836,6 +880,7 @@ class Chat(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -860,6 +905,7 @@ class Chat(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
entities=entities, entities=entities,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_media_group( def send_media_group(
@ -873,6 +919,7 @@ class Chat(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> List['Message']: ) -> List['Message']:
"""Shortcut for:: """Shortcut for::
@ -893,6 +940,7 @@ class Chat(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_chat_action( def send_chat_action(
@ -935,6 +983,7 @@ class Chat(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -960,6 +1009,7 @@ class Chat(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_contact( def send_contact(
@ -976,6 +1026,7 @@ class Chat(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1001,6 +1052,7 @@ class Chat(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_audio( def send_audio(
@ -1021,6 +1073,7 @@ class Chat(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1050,6 +1103,7 @@ class Chat(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_document( def send_document(
@ -1068,6 +1122,7 @@ class Chat(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1095,6 +1150,7 @@ class Chat(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
caption_entities=caption_entities, caption_entities=caption_entities,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_dice( def send_dice(
@ -1107,6 +1163,7 @@ class Chat(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1128,6 +1185,7 @@ class Chat(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_game( def send_game(
@ -1140,6 +1198,7 @@ class Chat(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1161,6 +1220,7 @@ class Chat(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_invoice( def send_invoice(
@ -1193,6 +1253,7 @@ class Chat(TelegramObject):
max_tip_amount: int = None, max_tip_amount: int = None,
suggested_tip_amounts: List[int] = None, suggested_tip_amounts: List[int] = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1242,6 +1303,7 @@ class Chat(TelegramObject):
max_tip_amount=max_tip_amount, max_tip_amount=max_tip_amount,
suggested_tip_amounts=suggested_tip_amounts, suggested_tip_amounts=suggested_tip_amounts,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_location( def send_location(
@ -1260,6 +1322,7 @@ class Chat(TelegramObject):
proximity_alert_radius: int = None, proximity_alert_radius: int = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1287,6 +1350,7 @@ class Chat(TelegramObject):
proximity_alert_radius=proximity_alert_radius, proximity_alert_radius=proximity_alert_radius,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_animation( def send_animation(
@ -1307,6 +1371,7 @@ class Chat(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1336,6 +1401,7 @@ class Chat(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_sticker( def send_sticker(
@ -1348,6 +1414,7 @@ class Chat(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1369,6 +1436,7 @@ class Chat(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_venue( def send_venue(
@ -1389,6 +1457,7 @@ class Chat(TelegramObject):
google_place_type: str = None, google_place_type: str = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1418,6 +1487,7 @@ class Chat(TelegramObject):
google_place_type=google_place_type, google_place_type=google_place_type,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_video( def send_video(
@ -1439,6 +1509,7 @@ class Chat(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1469,6 +1540,7 @@ class Chat(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_video_note( def send_video_note(
@ -1485,6 +1557,7 @@ class Chat(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1510,6 +1583,7 @@ class Chat(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_voice( def send_voice(
@ -1527,6 +1601,7 @@ class Chat(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1553,6 +1628,7 @@ class Chat(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_poll( def send_poll(
@ -1577,6 +1653,7 @@ class Chat(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1609,6 +1686,7 @@ class Chat(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
explanation_entities=explanation_entities, explanation_entities=explanation_entities,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_copy( def send_copy(
@ -1625,6 +1703,7 @@ class Chat(TelegramObject):
timeout: ODVInput[float] = DEFAULT_NONE, timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'MessageId': ) -> 'MessageId':
"""Shortcut for:: """Shortcut for::
@ -1650,6 +1729,7 @@ class Chat(TelegramObject):
timeout=timeout, timeout=timeout,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def copy_message( def copy_message(
@ -1666,6 +1746,7 @@ class Chat(TelegramObject):
timeout: ODVInput[float] = DEFAULT_NONE, timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'MessageId': ) -> 'MessageId':
"""Shortcut for:: """Shortcut for::
@ -1691,6 +1772,7 @@ class Chat(TelegramObject):
timeout=timeout, timeout=timeout,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def export_invite_link( def export_invite_link(
@ -1887,6 +1969,165 @@ class Chat(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
) )
def create_forum_topic(
self,
name: str,
icon_color: int = None,
icon_custom_emoji_id: str = None,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> "ForumTopic":
"""Shortcut for::
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:: 13.15
Returns:
:class:`telegram.ForumTopic`
"""
return self.bot.create_forum_topic(
chat_id=self.id,
name=name,
icon_color=icon_color,
icon_custom_emoji_id=icon_custom_emoji_id,
timeout=timeout,
api_kwargs=api_kwargs,
)
def edit_forum_topic(
self,
message_thread_id: int,
name: str,
icon_custom_emoji_id: str,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> bool:
"""Shortcut for::
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:: 13.15
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
"""
return self.bot.edit_forum_topic(
chat_id=self.id,
message_thread_id=message_thread_id,
name=name,
icon_custom_emoji_id=icon_custom_emoji_id,
timeout=timeout,
api_kwargs=api_kwargs,
)
def close_forum_topic(
self,
message_thread_id: int,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> bool:
"""Shortcut for::
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:: 13.15
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
"""
return self.bot.close_forum_topic(
chat_id=self.id,
message_thread_id=message_thread_id,
timeout=timeout,
api_kwargs=api_kwargs,
)
def reopen_forum_topic(
self,
message_thread_id: int,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> bool:
"""Shortcut for::
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:: 13.15
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
"""
return self.bot.reopen_forum_topic(
chat_id=self.id,
message_thread_id=message_thread_id,
timeout=timeout,
api_kwargs=api_kwargs,
)
def delete_forum_topic(
self,
message_thread_id: int,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> bool:
"""Shortcut for::
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:: 13.15
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
"""
return self.bot.delete_forum_topic(
chat_id=self.id,
message_thread_id=message_thread_id,
timeout=timeout,
api_kwargs=api_kwargs,
)
def unpin_all_forum_topic_messages(
self,
message_thread_id: int,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> bool:
"""Shortcut for::
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:: 13.15
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
"""
return self.bot.unpin_all_forum_topic_messages(
chat_id=self.id,
message_thread_id=message_thread_id,
timeout=timeout,
api_kwargs=api_kwargs,
)
def get_menu_button( def get_menu_button(
self, self,
timeout: ODVInput[float] = DEFAULT_NONE, timeout: ODVInput[float] = DEFAULT_NONE,

View file

@ -62,6 +62,10 @@ class ChatAdministratorRights(TelegramObject):
messages of other users. messages of other users.
can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to pin can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to pin
messages; groups and supergroups only. messages; groups and supergroups only.
can_manage_topics (:obj:`bool`, optional): :obj:`True`, if the user is allowed
to create, rename, close, and reopen forum topics; supergroups only.
.. versionadded:: 13.15
Attributes: Attributes:
is_anonymous (:obj:`bool`): :obj:`True`, if the user's presence in the chat is hidden. is_anonymous (:obj:`bool`): :obj:`True`, if the user's presence in the chat is hidden.
@ -89,6 +93,10 @@ class ChatAdministratorRights(TelegramObject):
messages of other users. messages of other users.
can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to pin can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to pin
messages; groups and supergroups only. messages; groups and supergroups only.
can_manage_topics (:obj:`bool`): Optional. :obj:`True`, if the user is allowed
to create, rename, close, and reopen forum topics; supergroups only.
.. versionadded:: 13.15
""" """
__slots__ = ( __slots__ = (
@ -103,6 +111,7 @@ class ChatAdministratorRights(TelegramObject):
'can_post_messages', 'can_post_messages',
'can_edit_messages', 'can_edit_messages',
'can_pin_messages', 'can_pin_messages',
'can_manage_topics',
'_id_attrs', '_id_attrs',
) )
@ -119,6 +128,7 @@ class ChatAdministratorRights(TelegramObject):
can_post_messages: bool = None, can_post_messages: bool = None,
can_edit_messages: bool = None, can_edit_messages: bool = None,
can_pin_messages: bool = None, can_pin_messages: bool = None,
can_manage_topics: bool = None,
**_kwargs: Any, **_kwargs: Any,
) -> None: ) -> None:
# Required # Required
@ -134,6 +144,7 @@ class ChatAdministratorRights(TelegramObject):
self.can_post_messages = can_post_messages self.can_post_messages = can_post_messages
self.can_edit_messages = can_edit_messages self.can_edit_messages = can_edit_messages
self.can_pin_messages = can_pin_messages self.can_pin_messages = can_pin_messages
self.can_manage_topics = can_manage_topics
self._id_attrs = ( self._id_attrs = (
self.is_anonymous, self.is_anonymous,
@ -156,7 +167,7 @@ class ChatAdministratorRights(TelegramObject):
:obj:`True`. This is e.g. useful when changing the bot's default administrator rights with :obj:`True`. This is e.g. useful when changing the bot's default administrator rights with
:meth:`telegram.Bot.set_my_default_administrator_rights`. :meth:`telegram.Bot.set_my_default_administrator_rights`.
""" """
return cls(True, True, True, True, True, True, True, True, True, True, True) return cls(True, True, True, True, True, True, True, True, True, True, True, True)
@classmethod @classmethod
def no_rights(cls) -> 'ChatAdministratorRights': def no_rights(cls) -> 'ChatAdministratorRights':
@ -164,4 +175,6 @@ class ChatAdministratorRights(TelegramObject):
This method returns the :class:`ChatAdministratorRights` object with all attributes set to This method returns the :class:`ChatAdministratorRights` object with all attributes set to
:obj:`False`. :obj:`False`.
""" """
return cls(False, False, False, False, False, False, False, False, False, False, False) return cls(
False, False, False, False, False, False, False, False, False, False, False, False
)

View file

@ -288,6 +288,7 @@ class ChatMember(TelegramObject):
'can_manage_voice_chats', 'can_manage_voice_chats',
'can_manage_video_chats', 'can_manage_video_chats',
'until_date', 'until_date',
'can_manage_topics',
'_id_attrs', '_id_attrs',
) )
@ -329,6 +330,7 @@ class ChatMember(TelegramObject):
can_manage_chat: bool = None, can_manage_chat: bool = None,
can_manage_voice_chats: bool = None, can_manage_voice_chats: bool = None,
can_manage_video_chats: bool = None, can_manage_video_chats: bool = None,
can_manage_topics: bool = None,
**_kwargs: Any, **_kwargs: Any,
): ):
# check before required to not waste resources if the error is raised # check before required to not waste resources if the error is raised
@ -371,6 +373,7 @@ class ChatMember(TelegramObject):
) )
self.can_manage_voice_chats = temp self.can_manage_voice_chats = temp
self.can_manage_video_chats = temp self.can_manage_video_chats = temp
self.can_manage_topics = can_manage_topics
self._id_attrs = (self.user, self.status) self._id_attrs = (self.user, self.status)
@ -494,6 +497,10 @@ class ChatMemberAdministrator(ChatMember):
new users to the chat. new users to the chat.
can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed
to pin messages; groups and supergroups only. to pin messages; groups and supergroups only.
can_manage_topics (:obj:`bool`, optional): :obj:`True`, if the user is allowed
to create, rename, close, and reopen forum topics; supergroups only.
.. versionadded:: 13.15
Attributes: Attributes:
status (:obj:`str`): The member's status in the chat, status (:obj:`str`): The member's status in the chat,
@ -536,6 +543,10 @@ class ChatMemberAdministrator(ChatMember):
new users to the chat. new users to the chat.
can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed
to pin messages; groups and supergroups only. to pin messages; groups and supergroups only.
can_manage_topics (:obj:`bool`): Optional. :obj:`True`, if the user is allowed
to create, rename, close, and reopen forum topics; supergroups only
.. versionadded:: 13.15
""" """
__slots__ = () __slots__ = ()
@ -557,6 +568,7 @@ class ChatMemberAdministrator(ChatMember):
can_invite_users: bool = None, can_invite_users: bool = None,
can_pin_messages: bool = None, can_pin_messages: bool = None,
can_manage_video_chats: bool = None, can_manage_video_chats: bool = None,
can_manage_topics: bool = None,
**_kwargs: Any, **_kwargs: Any,
): ):
super().__init__( super().__init__(
@ -576,6 +588,7 @@ class ChatMemberAdministrator(ChatMember):
can_invite_users=can_invite_users, can_invite_users=can_invite_users,
can_pin_messages=can_pin_messages, can_pin_messages=can_pin_messages,
can_manage_video_chats=can_manage_video_chats, can_manage_video_chats=can_manage_video_chats,
can_manage_topics=can_manage_topics,
) )
@ -631,6 +644,10 @@ class ChatMemberRestricted(ChatMember):
allowed to add web page previews to their messages. allowed to add web page previews to their messages.
until_date (:class:`datetime.datetime`, optional): Date when restrictions until_date (:class:`datetime.datetime`, optional): Date when restrictions
will be lifted for this user. will be lifted for this user.
can_manage_topics (:obj:`bool`): :obj:`True`, if the user is allowed to create
forum topics.
.. versionadded:: 13.15
Attributes: Attributes:
status (:obj:`str`): The member's status in the chat, status (:obj:`str`): The member's status in the chat,
@ -656,6 +673,10 @@ class ChatMemberRestricted(ChatMember):
allowed to add web page previews to their messages. allowed to add web page previews to their messages.
until_date (:class:`datetime.datetime`): Optional. Date when restrictions until_date (:class:`datetime.datetime`): Optional. Date when restrictions
will be lifted for this user. will be lifted for this user.
can_manage_topics (:obj:`bool`): :obj:`True`, if the user is allowed to create
forum topics.
.. versionadded:: 13.15
""" """
@ -674,6 +695,7 @@ class ChatMemberRestricted(ChatMember):
can_send_other_messages: bool = None, can_send_other_messages: bool = None,
can_add_web_page_previews: bool = None, can_add_web_page_previews: bool = None,
until_date: datetime.datetime = None, until_date: datetime.datetime = None,
can_manage_topics: bool = None,
**_kwargs: Any, **_kwargs: Any,
): ):
super().__init__( super().__init__(
@ -689,6 +711,7 @@ class ChatMemberRestricted(ChatMember):
can_send_other_messages=can_send_other_messages, can_send_other_messages=can_send_other_messages,
can_add_web_page_previews=can_add_web_page_previews, can_add_web_page_previews=can_add_web_page_previews,
until_date=until_date, until_date=until_date,
can_manage_topics=can_manage_topics,
) )

View file

@ -34,7 +34,7 @@ class ChatPermissions(TelegramObject):
Note: Note:
Though not stated explicitly in the official docs, Telegram changes not only the Though not stated explicitly in the official docs, Telegram changes not only the
permissions that are set, but also sets all the others to :obj:`False`. However, since not permissions that are set, but also sets all the others to :obj:`False`. However, since not
documented, this behaviour may change unbeknown to PTB. documented, this behavior may change unbeknown to PTB.
Args: Args:
can_send_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to send text can_send_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to send text
@ -55,6 +55,11 @@ class ChatPermissions(TelegramObject):
users to the chat. users to the chat.
can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to pin can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to pin
messages. Ignored in public supergroups. messages. Ignored in public supergroups.
can_manage_topics (:obj:`bool`, optional): :obj:`True`, if the user is allowed
to create forum topics. If omitted defaults to the value of
:attr:`can_pin_messages`.
.. versionadded:: 13.15
Attributes: Attributes:
can_send_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to send text can_send_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to send text
@ -75,6 +80,11 @@ class ChatPermissions(TelegramObject):
new users to the chat. new users to the chat.
can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to pin can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to pin
messages. Ignored in public supergroups. messages. Ignored in public supergroups.
can_manage_topics (:obj:`bool`): Optional. :obj:`True`, if the user is allowed
to create forum topics. If omitted defaults to the value of
:attr:`can_pin_messages`.
.. versionadded:: 13.15
""" """
@ -88,6 +98,7 @@ class ChatPermissions(TelegramObject):
'can_change_info', 'can_change_info',
'can_pin_messages', 'can_pin_messages',
'can_add_web_page_previews', 'can_add_web_page_previews',
'can_manage_topics',
) )
def __init__( def __init__(
@ -100,6 +111,7 @@ class ChatPermissions(TelegramObject):
can_change_info: bool = None, can_change_info: bool = None,
can_invite_users: bool = None, can_invite_users: bool = None,
can_pin_messages: bool = None, can_pin_messages: bool = None,
can_manage_topics: bool = None,
**_kwargs: Any, **_kwargs: Any,
): ):
# Required # Required
@ -111,6 +123,7 @@ class ChatPermissions(TelegramObject):
self.can_change_info = can_change_info self.can_change_info = can_change_info
self.can_invite_users = can_invite_users self.can_invite_users = can_invite_users
self.can_pin_messages = can_pin_messages self.can_pin_messages = can_pin_messages
self.can_manage_topics = can_manage_topics
self._id_attrs = ( self._id_attrs = (
self.can_send_messages, self.can_send_messages,

View file

@ -21,7 +21,7 @@ The following constants were extracted from the
`Telegram Bots API <https://core.telegram.org/bots/api>`_. `Telegram Bots API <https://core.telegram.org/bots/api>`_.
Attributes: Attributes:
BOT_API_VERSION (:obj:`str`): `6.2`. Telegram Bot API version supported by this BOT_API_VERSION (:obj:`str`): `6.3`. Telegram Bot API version supported by this
version of `python-telegram-bot`. Also available as ``telegram.bot_api_version``. version of `python-telegram-bot`. Also available as ``telegram.bot_api_version``.
.. versionadded:: 13.4 .. versionadded:: 13.4
@ -263,7 +263,7 @@ Attributes:
""" """
from typing import List from typing import List
BOT_API_VERSION: str = '6.2' BOT_API_VERSION: str = '6.3'
MAX_MESSAGE_LENGTH: int = 4096 MAX_MESSAGE_LENGTH: int = 4096
MAX_CAPTION_LENGTH: int = 1024 MAX_CAPTION_LENGTH: int = 1024
ANONYMOUS_ADMIN_ID: int = 1087968824 ANONYMOUS_ADMIN_ID: int = 1087968824

View file

@ -196,6 +196,7 @@ class ExtBot(telegram.bot.Bot):
timeout: ODVInput[float] = DEFAULT_NONE, timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> Union[bool, Message]: ) -> Union[bool, Message]:
# We override this method to call self._replace_keyboard and self._insert_callback_data. # We override this method to call self._replace_keyboard and self._insert_callback_data.
# This covers most methods that have a reply_markup # This covers most methods that have a reply_markup
@ -209,6 +210,7 @@ class ExtBot(telegram.bot.Bot):
timeout=timeout, timeout=timeout,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
if isinstance(result, Message): if isinstance(result, Message):
self._insert_callback_data(result) self._insert_callback_data(result)
@ -304,6 +306,7 @@ class ExtBot(telegram.bot.Bot):
timeout: ODVInput[float] = DEFAULT_NONE, timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> MessageId: ) -> MessageId:
# We override this method to call self._replace_keyboard # We override this method to call self._replace_keyboard
return super().copy_message( return super().copy_message(
@ -320,6 +323,7 @@ class ExtBot(telegram.bot.Bot):
timeout=timeout, timeout=timeout,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def get_chat( def get_chat(

View file

@ -1264,6 +1264,36 @@ officedocument.wordprocessingml.document")``.
web_app_data = _WebAppData() web_app_data = _WebAppData()
"""Messages that contain :attr:`telegram.Message.web_app_data`.""" """Messages that contain :attr:`telegram.Message.web_app_data`."""
class _ForumTopicCreated(MessageFilter):
__slots__ = ()
name = 'Filters.status_update.forum_topic_created'
def filter(self, message: Message) -> bool:
return bool(message.forum_topic_created)
forum_topic_created = _ForumTopicCreated()
"""Messages that contain :attr:`telegram.Message.forum_topic_created`."""
class _ForumTopicClosed(MessageFilter):
__slots__ = ()
name = 'Filters.status_update.forum_topic_closed'
def filter(self, message: Message) -> bool:
return bool(message.forum_topic_closed)
forum_topic_closed = _ForumTopicClosed()
"""Messages that contain :attr:`telegram.Message.forum_topic_closed`."""
class _ForumTopicReopened(MessageFilter):
__slots__ = ()
name = 'Filters.status_update.forum_topic_reopened'
def filter(self, message: Message) -> bool:
return bool(message.forum_topic_reopened)
forum_topic_reopened = _ForumTopicReopened()
"""Messages that contain :attr:`telegram.Message.forum_topic_reopened`."""
name = 'Filters.status_update' name = 'Filters.status_update'
def filter(self, message: Update) -> bool: def filter(self, message: Update) -> bool:
@ -1288,6 +1318,9 @@ officedocument.wordprocessingml.document")``.
or self.video_chat_ended(message) or self.video_chat_ended(message)
or self.video_chat_participants_invited(message) or self.video_chat_participants_invited(message)
or self.web_app_data(message) or self.web_app_data(message)
or self.forum_topic_created(message)
or self.forum_topic_closed(message)
or self.forum_topic_reopened(message)
) )
status_update = _StatusUpdate() status_update = _StatusUpdate()
@ -1361,6 +1394,22 @@ officedocument.wordprocessingml.document")``.
:attr:`telegram.Message.video_chat_participants_invited`. :attr:`telegram.Message.video_chat_participants_invited`.
.. versionadded:: 13.12 .. versionadded:: 13.12
web_app_data: Messages that contain
:attr:`telegram.Message.web_app_data`.
.. versionadded:: 13.12
forum_topic_created: Messages that contain
:attr:`telegram.Message.forum_topic_created`.
.. versionadded:: 13.15
forum_topic_closed: Messages that contain
:attr:`telegram.Message.forum_topic_closed`.
.. versionadded:: 13.15
forum_topic_reopened: Messages that contain
:attr:`telegram.Message.forum_topic_reopened`.
.. versionadded:: 13.15
""" """
@ -2212,6 +2261,19 @@ officedocument.wordprocessingml.document")``.
.. versionadded:: 13.9 .. versionadded:: 13.9
""" """
class _IsTopicMessage(MessageFilter):
__slots__ = ()
name = 'Filters.is_topic_message'
def filter(self, message: Message) -> bool:
return bool(message.is_topic_message)
is_topic_message = _IsTopicMessage()
"""Messages that contain :attr:`telegram.Message.is_topic_message`.
.. versionadded:: 13.15
"""
class _HasProtectedContent(MessageFilter): class _HasProtectedContent(MessageFilter):
__slots__ = () __slots__ = ()
name = 'Filters.has_protected_content' name = 'Filters.has_protected_content'

134
telegram/forumtopic.py Normal file
View file

@ -0,0 +1,134 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2022
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains objects related to Telegram forum topics."""
from typing import Any
from telegram import TelegramObject
class ForumTopic(TelegramObject):
"""
This object represents a forum topic.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`message_thread_id`, :attr:`name` and :attr:`icon_color`
are equal.
.. versionadded:: 13.15
Args:
message_thread_id (:obj:`int`): Unique identifier of the forum topic
name (:obj:`str`): Name of the topic
icon_color (:obj:`int`): Color of the topic icon in RGB format
icon_custom_emoji_id (:obj:`str`, optional): Unique identifier of the custom emoji shown
as the topic icon.
Attributes:
message_thread_id (:obj:`int`): Unique identifier of the forum topic
name (:obj:`str`): Name of the topic
icon_color (:obj:`int`): Color of the topic icon in RGB format
icon_custom_emoji_id (:obj:`str`): Optional. Unique identifier of the custom emoji shown
as the topic icon.
"""
__slots__ = ("message_thread_id", "name", "icon_color", "icon_custom_emoji_id")
def __init__(
self,
message_thread_id: int,
name: str,
icon_color: int,
icon_custom_emoji_id: str = None,
**_kwargs: Any,
):
self.message_thread_id = message_thread_id
self.name = name
self.icon_color = icon_color
self.icon_custom_emoji_id = icon_custom_emoji_id
self._id_attrs = (self.message_thread_id, self.name, self.icon_color)
class ForumTopicCreated(TelegramObject):
"""
This object represents the content of a service message about a new forum topic created in
the chat.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`name` and :attr:`icon_color` are equal.
.. versionadded:: 13.15
Args:
name (:obj:`str`): Name of the topic
icon_color (:obj:`int`): Color of the topic icon in RGB format
icon_custom_emoji_id (:obj:`str`, optional): Unique identifier of the custom emoji shown
as the topic icon.
Attributes:
name (:obj:`str`): Name of the topic
icon_color (:obj:`int`): Color of the topic icon in RGB format
icon_custom_emoji_id (:obj:`str`): Optional. Unique identifier of the custom emoji shown
as the topic icon.
"""
__slots__ = ("name", "icon_color", "icon_custom_emoji_id", "_id_attrs")
def __init__(
self,
name: str,
icon_color: int,
icon_custom_emoji_id: str = None,
**_kwargs: Any,
):
self.name = name
self.icon_color = icon_color
self.icon_custom_emoji_id = icon_custom_emoji_id
self._id_attrs = (self.name, self.icon_color)
class ForumTopicClosed(TelegramObject):
"""
This object represents a service message about a forum topic closed in the chat.
Currently holds no information.
.. versionadded:: 13.15
"""
__slots__ = ()
def __init__(self, **_kwargs: Any): # skipcq: PTC-W0049
pass
class ForumTopicReopened(TelegramObject):
"""
This object represents a service message about a forum topic reopened in the chat.
Currently holds no information.
.. versionadded:: 13.15
"""
__slots__ = ()
def __init__(self, **_kwargs: Any): # skipcq: PTC-W0049
pass

View file

@ -60,6 +60,7 @@ from telegram import (
WebAppData, WebAppData,
VideoChatScheduled, VideoChatScheduled,
) )
from telegram.forumtopic import ForumTopicClosed, ForumTopicCreated, ForumTopicReopened
from telegram.utils.helpers import ( from telegram.utils.helpers import (
escape_markdown, escape_markdown,
from_timestamp, from_timestamp,
@ -263,6 +264,26 @@ class Message(TelegramObject):
.. versionadded:: 13.12 .. versionadded:: 13.12
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
to the message. ``login_url`` buttons are represented as ordinary url buttons. to the message. ``login_url`` buttons are represented as ordinary url buttons.
is_topic_message (:obj:`bool`, optional): :obj:`True`, if the message is sent to a forum
topic.
.. versionadded:: 13.15
message_thread_id (:obj:`int`, optional): Unique identifier of a message thread to which
the message belongs; for supergroups only.
.. versionadded:: 13.15
forum_topic_created (:class:`telegram.ForumTopicCreated`, optional): Service message:
forum topic created
.. versionadded:: 13.15
forum_topic_closed (:class:`telegram.ForumTopicClosed`, optional): Service message:
forum topic closed
.. versionadded:: 13.15
forum_topic_reopened (:class:`telegram.ForumTopicReopened`, optional): Service message:
forum topic reopened
.. versionadded:: 13.15
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
Attributes: Attributes:
@ -405,6 +426,26 @@ class Message(TelegramObject):
.. versionadded:: 13.12 .. versionadded:: 13.12
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
to the message. to the message.
is_topic_message (:obj:`bool`): Optional. :obj:`True`, if the message is sent to a forum
topic.
.. versionadded:: 13.15
message_thread_id (:obj:`int`): Optional. Unique identifier of a message thread to which
the message belongs; for supergroups only.
.. versionadded:: 13.15
forum_topic_created (:class:`telegram.ForumTopicCreated`): Optional. Service message:
forum topic created
.. versionadded:: 13.15
forum_topic_closed (:class:`telegram.ForumTopicClosed`): Optional. Service message:
forum topic closed
.. versionadded:: 13.15
forum_topic_reopened (:class:`telegram.ForumTopicReopened`): Optional. Service message:
forum topic reopened
.. versionadded:: 13.15
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
.. |custom_emoji_formatting_note| replace:: Custom emoji entities will currently be ignored .. |custom_emoji_formatting_note| replace:: Custom emoji entities will currently be ignored
@ -478,6 +519,11 @@ class Message(TelegramObject):
'is_automatic_forward', 'is_automatic_forward',
'has_protected_content', 'has_protected_content',
'web_app_data', 'web_app_data',
'is_topic_message',
'message_thread_id',
'forum_topic_created',
'forum_topic_closed',
'forum_topic_reopened',
'_id_attrs', '_id_attrs',
) )
@ -587,6 +633,11 @@ class Message(TelegramObject):
video_chat_ended: VideoChatEnded = None, video_chat_ended: VideoChatEnded = None,
video_chat_participants_invited: VideoChatParticipantsInvited = None, video_chat_participants_invited: VideoChatParticipantsInvited = None,
web_app_data: WebAppData = None, web_app_data: WebAppData = None,
is_topic_message: bool = None,
message_thread_id: int = None,
forum_topic_created: ForumTopicCreated = None,
forum_topic_closed: ForumTopicClosed = None,
forum_topic_reopened: ForumTopicReopened = None,
**_kwargs: Any, **_kwargs: Any,
): ):
if ( if (
@ -690,7 +741,11 @@ class Message(TelegramObject):
self.voice_chat_participants_invited = temp3 self.voice_chat_participants_invited = temp3
self.video_chat_participants_invited = temp3 self.video_chat_participants_invited = temp3
self.web_app_data = web_app_data self.web_app_data = web_app_data
self.is_topic_message = is_topic_message
self.message_thread_id = message_thread_id
self.forum_topic_created = forum_topic_created
self.forum_topic_closed = forum_topic_closed
self.forum_topic_reopened = forum_topic_reopened
self.bot = bot self.bot = bot
self._effective_attachment = DEFAULT_NONE self._effective_attachment = DEFAULT_NONE
@ -781,6 +836,13 @@ class Message(TelegramObject):
data.get('video_chat_participants_invited'), bot data.get('video_chat_participants_invited'), bot
) )
data['web_app_data'] = WebAppData.de_json(data.get('web_app_data'), bot) data['web_app_data'] = WebAppData.de_json(data.get('web_app_data'), bot)
data["forum_topic_closed"] = ForumTopicClosed.de_json(data.get("forum_topic_closed"), bot)
data["forum_topic_created"] = ForumTopicCreated.de_json(
data.get("forum_topic_created"), bot
)
data["forum_topic_reopened"] = ForumTopicReopened.de_json(
data.get("forum_topic_reopened"), bot
)
return cls(bot=bot, **data) return cls(bot=bot, **data)
@ -893,6 +955,7 @@ class Message(TelegramObject):
entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -924,6 +987,7 @@ class Message(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
entities=entities, entities=entities,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_markdown( def reply_markdown(
@ -939,6 +1003,7 @@ class Message(TelegramObject):
entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -980,6 +1045,7 @@ class Message(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
entities=entities, entities=entities,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_markdown_v2( def reply_markdown_v2(
@ -995,6 +1061,7 @@ class Message(TelegramObject):
entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1032,6 +1099,7 @@ class Message(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
entities=entities, entities=entities,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_html( def reply_html(
@ -1047,6 +1115,7 @@ class Message(TelegramObject):
entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1084,6 +1153,7 @@ class Message(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
entities=entities, entities=entities,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_media_group( def reply_media_group(
@ -1098,6 +1168,7 @@ class Message(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> List['Message']: ) -> List['Message']:
"""Shortcut for:: """Shortcut for::
@ -1127,6 +1198,7 @@ class Message(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_photo( def reply_photo(
@ -1144,6 +1216,7 @@ class Message(TelegramObject):
filename: str = None, filename: str = None,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1176,6 +1249,7 @@ class Message(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_audio( def reply_audio(
@ -1197,6 +1271,7 @@ class Message(TelegramObject):
filename: str = None, filename: str = None,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1233,6 +1308,7 @@ class Message(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_document( def reply_document(
@ -1252,6 +1328,7 @@ class Message(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1286,6 +1363,7 @@ class Message(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
caption_entities=caption_entities, caption_entities=caption_entities,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_animation( def reply_animation(
@ -1307,6 +1385,7 @@ class Message(TelegramObject):
filename: str = None, filename: str = None,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1343,6 +1422,7 @@ class Message(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_sticker( def reply_sticker(
@ -1356,6 +1436,7 @@ class Message(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1384,6 +1465,7 @@ class Message(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_video( def reply_video(
@ -1406,6 +1488,7 @@ class Message(TelegramObject):
filename: str = None, filename: str = None,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1443,6 +1526,7 @@ class Message(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_video_note( def reply_video_note(
@ -1460,6 +1544,7 @@ class Message(TelegramObject):
filename: str = None, filename: str = None,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1492,6 +1577,7 @@ class Message(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_voice( def reply_voice(
@ -1510,6 +1596,7 @@ class Message(TelegramObject):
filename: str = None, filename: str = None,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1543,6 +1630,7 @@ class Message(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_location( def reply_location(
@ -1562,6 +1650,7 @@ class Message(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1596,6 +1685,7 @@ class Message(TelegramObject):
proximity_alert_radius=proximity_alert_radius, proximity_alert_radius=proximity_alert_radius,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_venue( def reply_venue(
@ -1617,6 +1707,7 @@ class Message(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1653,6 +1744,7 @@ class Message(TelegramObject):
google_place_type=google_place_type, google_place_type=google_place_type,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_contact( def reply_contact(
@ -1670,6 +1762,7 @@ class Message(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1702,6 +1795,7 @@ class Message(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_poll( def reply_poll(
@ -1726,6 +1820,7 @@ class Message(TelegramObject):
explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1765,6 +1860,7 @@ class Message(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
explanation_entities=explanation_entities, explanation_entities=explanation_entities,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_dice( def reply_dice(
@ -1778,6 +1874,7 @@ class Message(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1806,6 +1903,7 @@ class Message(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_chat_action( def reply_chat_action(
@ -1844,6 +1942,7 @@ class Message(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1874,6 +1973,7 @@ class Message(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_invoice( def reply_invoice(
@ -1907,6 +2007,7 @@ class Message(TelegramObject):
max_tip_amount: int = None, max_tip_amount: int = None,
suggested_tip_amounts: List[int] = None, suggested_tip_amounts: List[int] = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1965,6 +2066,7 @@ class Message(TelegramObject):
max_tip_amount=max_tip_amount, max_tip_amount=max_tip_amount,
suggested_tip_amounts=suggested_tip_amounts, suggested_tip_amounts=suggested_tip_amounts,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def forward( def forward(
@ -1974,6 +2076,7 @@ class Message(TelegramObject):
timeout: ODVInput[float] = DEFAULT_NONE, timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -2005,6 +2108,7 @@ class Message(TelegramObject):
timeout=timeout, timeout=timeout,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def copy( def copy(
@ -2020,6 +2124,7 @@ class Message(TelegramObject):
timeout: ODVInput[float] = DEFAULT_NONE, timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'MessageId': ) -> 'MessageId':
"""Shortcut for:: """Shortcut for::
@ -2049,6 +2154,7 @@ class Message(TelegramObject):
timeout=timeout, timeout=timeout,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def reply_copy( def reply_copy(
@ -2066,6 +2172,7 @@ class Message(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
quote: bool = None, quote: bool = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'MessageId': ) -> 'MessageId':
"""Shortcut for:: """Shortcut for::
@ -2104,6 +2211,7 @@ class Message(TelegramObject):
timeout=timeout, timeout=timeout,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def edit_text( def edit_text(
@ -2517,6 +2625,145 @@ class Message(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
) )
def edit_forum_topic(
self,
name: str,
icon_custom_emoji_id: str,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> bool:
"""Shortcut for::
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:: 13.15
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
"""
return self.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,
timeout=timeout,
api_kwargs=api_kwargs,
)
def close_forum_topic(
self,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> bool:
"""Shortcut for::
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:: 13.15
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
"""
return self.bot.close_forum_topic(
chat_id=self.chat_id,
message_thread_id=self.message_thread_id,
timeout=timeout,
api_kwargs=api_kwargs,
)
def reopen_forum_topic(
self,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> bool:
"""Shortcut for::
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:: 13.15
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
"""
return self.bot.reopen_forum_topic(
chat_id=self.chat_id,
message_thread_id=self.message_thread_id,
timeout=timeout,
api_kwargs=api_kwargs,
)
def delete_forum_topic(
self,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> bool:
"""Shortcut for::
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:: 13.15
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
"""
return self.bot.delete_forum_topic(
chat_id=self.chat_id,
message_thread_id=self.message_thread_id,
timeout=timeout,
api_kwargs=api_kwargs,
)
def unpin_all_forum_topic_messages(
self,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> bool:
"""Shortcut for::
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:: 13.15
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
"""
return self.bot.unpin_all_forum_topic_messages(
chat_id=self.chat_id,
message_thread_id=self.message_thread_id,
timeout=timeout,
api_kwargs=api_kwargs,
)
def parse_entity(self, entity: MessageEntity) -> str: def parse_entity(self, entity: MessageEntity) -> str:
"""Returns the text from a given :class:`telegram.MessageEntity`. """Returns the text from a given :class:`telegram.MessageEntity`.

View file

@ -360,6 +360,7 @@ class User(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -384,6 +385,7 @@ class User(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
entities=entities, entities=entities,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_photo( def send_photo(
@ -400,6 +402,7 @@ class User(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -425,6 +428,7 @@ class User(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_media_group( def send_media_group(
@ -438,6 +442,7 @@ class User(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> List['Message']: ) -> List['Message']:
"""Shortcut for:: """Shortcut for::
@ -458,6 +463,7 @@ class User(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_audio( def send_audio(
@ -478,6 +484,7 @@ class User(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -507,6 +514,7 @@ class User(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_chat_action( def send_chat_action(
@ -549,6 +557,7 @@ class User(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -574,6 +583,7 @@ class User(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_dice( def send_dice(
@ -586,6 +596,7 @@ class User(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -607,6 +618,7 @@ class User(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_document( def send_document(
@ -625,6 +637,7 @@ class User(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -652,6 +665,7 @@ class User(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
caption_entities=caption_entities, caption_entities=caption_entities,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_game( def send_game(
@ -664,6 +678,7 @@ class User(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -685,6 +700,7 @@ class User(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_invoice( def send_invoice(
@ -717,6 +733,7 @@ class User(TelegramObject):
max_tip_amount: int = None, max_tip_amount: int = None,
suggested_tip_amounts: List[int] = None, suggested_tip_amounts: List[int] = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -766,6 +783,7 @@ class User(TelegramObject):
max_tip_amount=max_tip_amount, max_tip_amount=max_tip_amount,
suggested_tip_amounts=suggested_tip_amounts, suggested_tip_amounts=suggested_tip_amounts,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_location( def send_location(
@ -784,6 +802,7 @@ class User(TelegramObject):
proximity_alert_radius: int = None, proximity_alert_radius: int = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -811,6 +830,7 @@ class User(TelegramObject):
proximity_alert_radius=proximity_alert_radius, proximity_alert_radius=proximity_alert_radius,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_animation( def send_animation(
@ -831,6 +851,7 @@ class User(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -860,6 +881,7 @@ class User(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_sticker( def send_sticker(
@ -872,6 +894,7 @@ class User(TelegramObject):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -893,6 +916,7 @@ class User(TelegramObject):
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_video( def send_video(
@ -914,6 +938,7 @@ class User(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -944,6 +969,7 @@ class User(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_venue( def send_venue(
@ -964,6 +990,7 @@ class User(TelegramObject):
google_place_type: str = None, google_place_type: str = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -993,6 +1020,7 @@ class User(TelegramObject):
google_place_type=google_place_type, google_place_type=google_place_type,
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_video_note( def send_video_note(
@ -1009,6 +1037,7 @@ class User(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1034,6 +1063,7 @@ class User(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_voice( def send_voice(
@ -1051,6 +1081,7 @@ class User(TelegramObject):
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
filename: str = None, filename: str = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1077,6 +1108,7 @@ class User(TelegramObject):
caption_entities=caption_entities, caption_entities=caption_entities,
filename=filename, filename=filename,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_poll( def send_poll(
@ -1101,6 +1133,7 @@ class User(TelegramObject):
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'Message': ) -> 'Message':
"""Shortcut for:: """Shortcut for::
@ -1133,6 +1166,7 @@ class User(TelegramObject):
allow_sending_without_reply=allow_sending_without_reply, allow_sending_without_reply=allow_sending_without_reply,
explanation_entities=explanation_entities, explanation_entities=explanation_entities,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def send_copy( def send_copy(
@ -1149,6 +1183,7 @@ class User(TelegramObject):
timeout: ODVInput[float] = DEFAULT_NONE, timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'MessageId': ) -> 'MessageId':
"""Shortcut for:: """Shortcut for::
@ -1174,6 +1209,7 @@ class User(TelegramObject):
timeout=timeout, timeout=timeout,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def copy_message( def copy_message(
@ -1190,6 +1226,7 @@ class User(TelegramObject):
timeout: ODVInput[float] = DEFAULT_NONE, timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
protect_content: bool = None, protect_content: bool = None,
message_thread_id: int = None,
) -> 'MessageId': ) -> 'MessageId':
"""Shortcut for:: """Shortcut for::
@ -1215,6 +1252,7 @@ class User(TelegramObject):
timeout=timeout, timeout=timeout,
api_kwargs=api_kwargs, api_kwargs=api_kwargs,
protect_content=protect_content, protect_content=protect_content,
message_thread_id=message_thread_id,
) )
def approve_join_request( def approve_join_request(

View file

@ -29,15 +29,16 @@ from telegram.error import RetryAfter, TimedOut
# These bots are only able to talk in our test chats, so they are quite useless for other # These bots are only able to talk in our test chats, so they are quite useless for other
# purposes than testing. # purposes than testing.
FALLBACKS = ( FALLBACKS = (
'W3sidG9rZW4iOiAiNTc5Njk0NzE0OkFBRnBLOHc2emtrVXJENHhTZVl3RjNNTzhlLTRHcm1jeTdjIiwgInBheW1lbnRfc' "W3sidG9rZW4iOiAiNTc5Njk0NzE0OkFBRnBLOHc2emtrVXJENHhTZVl3RjNNTzhlLTRHcm1jeTdjIiwgInBheW1lbnRfc"
'HJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TmpRME5qWmxOekk1WWpKaSIsICJjaGF0X2lkIjogIjY3NTY2Nj' "HJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TmpRME5qWmxOekk1WWpKaSIsICJjaGF0X2lkIjogIjY3NTY2Nj"
'IyNCIsICJzdXBlcl9ncm91cF9pZCI6ICItMTAwMTMxMDkxMTEzNSIsICJjaGFubmVsX2lkIjogIkBweXRob250ZWxlZ3J' "IyNCIsICJzdXBlcl9ncm91cF9pZCI6ICItMTAwMTMxMDkxMTEzNSIsICJmb3J1bV9ncm91cF9pZCI6ICItMTAwMTYxOTE"
'hbWJvdHRlc3RzIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBmYWxsYmFjayAxIiwgImJvdF91c2VybmFtZSI6ICJAcHRi' "1OTQwNCIsICJjaGFubmVsX2lkIjogIkBweXRob250ZWxlZ3JhbWJvdHRlc3RzIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0"
'X2ZhbGxiYWNrXzFfYm90In0sIHsidG9rZW4iOiAiNTU4MTk0MDY2OkFBRndEUElGbHpHVWxDYVdIdFRPRVg0UkZyWDh1O' "cyBmYWxsYmFjayAxIiwgImJvdF91c2VybmFtZSI6ICJAcHRiX2ZhbGxiYWNrXzFfYm90In0sIHsidG9rZW4iOiAiNTU4M"
'URNcWZvIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WWpFd09EUXdNVEZtTkRjeSIsIC' "Tk0MDY2OkFBRndEUElGbHpHVWxDYVdIdFRPRVg0UkZyWDh1OURNcWZvIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOi"
'JjaGF0X2lkIjogIjY3NTY2NjIyNCIsICJzdXBlcl9ncm91cF9pZCI6ICItMTAwMTIyMTIxNjgzMCIsICJjaGFubmVsX2l' "AiMjg0Njg1MDYzOlRFU1Q6WWpFd09EUXdNVEZtTkRjeSIsICJjaGF0X2lkIjogIjY3NTY2NjIyNCIsICJzdXBlcl9ncm9"
'kIjogIkBweXRob250ZWxlZ3JhbWJvdHRlc3RzIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBmYWxsYmFjayAyIiwgImJv' "1cF9pZCI6ICItMTAwMTIyMTIxNjgzMCIsICJmb3J1bV9ncm91cF9pZCI6ICItMTAwMTYxOTE1OTQwNCIsICJjaGFubmVs"
'dF91c2VybmFtZSI6ICJAcHRiX2ZhbGxiYWNrXzJfYm90In1d' "X2lkIjogIkBweXRob250ZWxlZ3JhbWJvdHRlc3RzIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBmYWxsYmFjayAyIiwgI"
"mJvdF91c2VybmFtZSI6ICJAcHRiX2ZhbGxiYWNrXzJfYm90In1d "
) )
GITHUB_ACTION = os.getenv('GITHUB_ACTION', None) GITHUB_ACTION = os.getenv('GITHUB_ACTION', None)

View file

@ -138,6 +138,11 @@ def super_group_id(bot_info):
return bot_info['super_group_id'] return bot_info['super_group_id']
@pytest.fixture(scope="session")
def forum_group_id(bot_info):
return int(bot_info["forum_group_id"])
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def channel_id(bot_info): def channel_id(bot_info):
return bot_info['channel_id'] return bot_info['channel_id']

View file

@ -1851,6 +1851,7 @@ class TestBot:
can_promote_members=True, can_promote_members=True,
can_manage_chat=True, can_manage_chat=True,
can_manage_voice_chats=True, can_manage_voice_chats=True,
can_manage_topics=True,
) )
with pytest.raises( with pytest.raises(
@ -1892,6 +1893,7 @@ class TestBot:
and data.get('can_promote_members') == 9 and data.get('can_promote_members') == 9
and data.get('can_manage_chat') == 10 and data.get('can_manage_chat') == 10
and data.get('can_manage_video_chats') == 11 and data.get('can_manage_video_chats') == 11
and data.get("can_manage_topics") == 12
) )
monkeypatch.setattr(bot, '_post', make_assertion) monkeypatch.setattr(bot, '_post', make_assertion)
@ -1909,6 +1911,7 @@ class TestBot:
can_promote_members=9, can_promote_members=9,
can_manage_chat=10, can_manage_chat=10,
can_manage_voice_chats=11, can_manage_voice_chats=11,
can_manage_topics=12,
) )
# Test that video_chats also works # Test that video_chats also works
@ -2172,6 +2175,9 @@ class TestBot:
# set_sticker_position_in_set, delete_sticker_from_set and get_custom_emoji_stickers # set_sticker_position_in_set, delete_sticker_from_set and get_custom_emoji_stickers
# are tested in the test_sticker module. # are tested in the test_sticker module.
# get_forum_topic_icon_stickers, edit_forum_topic, etc...
# are tested in the test_forum module.
def test_timeout_propagation_explicit(self, monkeypatch, bot, chat_id): def test_timeout_propagation_explicit(self, monkeypatch, bot, chat_id):
from telegram.vendor.ptb_urllib3.urllib3.util.timeout import Timeout from telegram.vendor.ptb_urllib3.urllib3.util.timeout import Timeout
@ -2307,6 +2313,7 @@ class TestBot:
assert my_admin_rights_ch.can_promote_members is my_rights.can_promote_members assert my_admin_rights_ch.can_promote_members is my_rights.can_promote_members
assert my_admin_rights_ch.can_restrict_members is my_rights.can_restrict_members assert my_admin_rights_ch.can_restrict_members is my_rights.can_restrict_members
assert my_admin_rights_ch.can_pin_messages is None # Not returned for channels assert my_admin_rights_ch.can_pin_messages is None # Not returned for channels
assert my_admin_rights_ch.can_manage_topics is None # Not returned for channels
def test_get_set_chat_menu_button(self, bot, chat_id): def test_get_set_chat_menu_button(self, bot, chat_id):
# Test our chat menu button is commands- # Test our chat menu button is commands-
@ -2440,6 +2447,7 @@ class TestBot:
assert data["disable_notification"] is True assert data["disable_notification"] is True
assert data["caption_entities"] == [MessageEntity(MessageEntity.BOLD, 0, 4)] assert data["caption_entities"] == [MessageEntity(MessageEntity.BOLD, 0, 4)]
assert data['protect_content'] is True assert data['protect_content'] is True
assert data["message_thread_id"] == 1
return data return data
monkeypatch.setattr(bot.request, 'post', post) monkeypatch.setattr(bot.request, 'post', post)
@ -2454,6 +2462,7 @@ class TestBot:
reply_markup=keyboard.to_json() if json_keyboard else keyboard, reply_markup=keyboard.to_json() if json_keyboard else keyboard,
disable_notification=True, disable_notification=True,
protect_content=True, protect_content=True,
message_thread_id=1,
) )
@flaky(3, 1) @flaky(3, 1)

View file

@ -46,6 +46,9 @@ def chat(bot):
join_to_send_messages=True, join_to_send_messages=True,
join_by_request=True, join_by_request=True,
has_restricted_voice_and_video_messages=True, has_restricted_voice_and_video_messages=True,
is_forum=True,
active_usernames=TestChat.active_usernames,
emoji_status_custom_emoji_id=TestChat.emoji_status_custom_emoji_id,
) )
@ -72,6 +75,9 @@ class TestChat:
join_to_send_messages = True join_to_send_messages = True
join_by_request = True join_by_request = True
has_restricted_voice_and_video_messages = True has_restricted_voice_and_video_messages = True
is_forum = True
active_usernames = ["These", "Are", "Usernames!"]
emoji_status_custom_emoji_id = "VeryUniqueCustomEmojiID"
def test_slot_behaviour(self, chat, recwarn, mro_slots): def test_slot_behaviour(self, chat, recwarn, mro_slots):
for attr in chat.__slots__: for attr in chat.__slots__:
@ -103,6 +109,9 @@ class TestChat:
'has_restricted_voice_and_video_messages': ( 'has_restricted_voice_and_video_messages': (
self.has_restricted_voice_and_video_messages self.has_restricted_voice_and_video_messages
), ),
"is_forum": self.is_forum,
"active_usernames": self.active_usernames,
"emoji_status_custom_emoji_id": self.emoji_status_custom_emoji_id,
} }
chat = Chat.de_json(json_dict, bot) chat = Chat.de_json(json_dict, bot)
@ -128,6 +137,9 @@ class TestChat:
chat.has_restricted_voice_and_video_messages chat.has_restricted_voice_and_video_messages
== self.has_restricted_voice_and_video_messages == self.has_restricted_voice_and_video_messages
) )
assert chat.is_forum == self.is_forum
assert chat.active_usernames == self.active_usernames
assert chat.emoji_status_custom_emoji_id == self.emoji_status_custom_emoji_id
def test_to_dict(self, chat): def test_to_dict(self, chat):
chat_dict = chat.to_dict() chat_dict = chat.to_dict()
@ -152,6 +164,9 @@ class TestChat:
chat_dict["has_restricted_voice_and_video_messages"] chat_dict["has_restricted_voice_and_video_messages"]
== chat.has_restricted_voice_and_video_messages == chat.has_restricted_voice_and_video_messages
) )
assert chat_dict["is_forum"] == chat.is_forum
assert chat_dict["active_usernames"] == chat.active_usernames
assert chat_dict["emoji_status_custom_emoji_id"] == chat.emoji_status_custom_emoji_id
def test_link(self, chat): def test_link(self, chat):
assert chat.link == f'https://t.me/{chat.username}' assert chat.link == f'https://t.me/{chat.username}'
@ -795,6 +810,128 @@ class TestChat:
monkeypatch.setattr(chat.bot, 'decline_chat_join_request', make_assertion) monkeypatch.setattr(chat.bot, 'decline_chat_join_request', make_assertion)
assert chat.decline_join_request(user_id=42) assert chat.decline_join_request(user_id=42)
def test_create_forum_topic(self, monkeypatch, chat):
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 check_shortcut_call(
chat.create_forum_topic,
chat.bot,
"create_forum_topic",
shortcut_kwargs=["chat_id"],
)
assert check_defaults_handling(chat.create_forum_topic, chat.bot)
monkeypatch.setattr(chat.bot, "create_forum_topic", make_assertion)
assert chat.create_forum_topic(
name="New Name", icon_color=0x6FB9F0, icon_custom_emoji_id="12345"
)
def test_edit_forum_topic(self, monkeypatch, chat):
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 check_shortcut_call(
chat.edit_forum_topic, chat.bot, "edit_forum_topic", shortcut_kwargs=["chat_id"]
)
assert check_defaults_handling(chat.edit_forum_topic, chat.bot)
monkeypatch.setattr(chat.bot, "edit_forum_topic", make_assertion)
assert chat.edit_forum_topic(
message_thread_id=42, name="New Name", icon_custom_emoji_id="12345"
)
def test_close_forum_topic(self, monkeypatch, chat):
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 check_shortcut_call(
chat.close_forum_topic,
chat.bot,
"close_forum_topic",
shortcut_kwargs=["chat_id"],
)
assert check_defaults_handling(chat.close_forum_topic, chat.bot)
monkeypatch.setattr(chat.bot, "close_forum_topic", make_assertion)
assert chat.close_forum_topic(message_thread_id=42)
def test_reopen_forum_topic(self, monkeypatch, chat):
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 check_shortcut_call(
chat.reopen_forum_topic,
chat.bot,
"reopen_forum_topic",
shortcut_kwargs=["chat_id"],
)
assert check_defaults_handling(chat.reopen_forum_topic, chat.bot)
monkeypatch.setattr(chat.bot, "reopen_forum_topic", make_assertion)
assert chat.reopen_forum_topic(message_thread_id=42)
def test_delete_forum_topic(self, monkeypatch, chat):
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 check_shortcut_call(
chat.delete_forum_topic,
chat.bot,
"delete_forum_topic",
shortcut_kwargs=["chat_id"],
)
assert check_defaults_handling(chat.delete_forum_topic, chat.bot)
monkeypatch.setattr(chat.bot, "delete_forum_topic", make_assertion)
assert chat.delete_forum_topic(message_thread_id=42)
def test_unpin_all_forum_topic_messages(self, monkeypatch, chat):
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 check_shortcut_call(
chat.unpin_all_forum_topic_messages,
chat.bot,
"unpin_all_forum_topic_messages",
shortcut_kwargs=["chat_id"],
)
assert check_defaults_handling(chat.unpin_all_forum_topic_messages, chat.bot)
monkeypatch.setattr(chat.bot, "unpin_all_forum_topic_messages", make_assertion)
assert chat.unpin_all_forum_topic_messages(message_thread_id=42)
def test_equality(self): def test_equality(self):
a = Chat(self.id_, self.title, self.type_) a = Chat(self.id_, self.title, self.type_)
b = Chat(self.id_, self.title, self.type_) b = Chat(self.id_, self.title, self.type_)

View file

@ -34,6 +34,7 @@ def chat_admin_rights():
can_edit_messages=True, can_edit_messages=True,
can_manage_chat=True, can_manage_chat=True,
can_manage_video_chats=True, can_manage_video_chats=True,
can_manage_topics=True,
is_anonymous=True, is_anonymous=True,
) )
@ -57,6 +58,7 @@ class TestChatAdministratorRights:
'can_edit_messages': True, 'can_edit_messages': True,
'can_manage_chat': True, 'can_manage_chat': True,
'can_manage_video_chats': True, 'can_manage_video_chats': True,
"can_manage_topics": True,
'is_anonymous': True, 'is_anonymous': True,
} }
chat_administrator_rights_de = ChatAdministratorRights.de_json(json_dict, bot) chat_administrator_rights_de = ChatAdministratorRights.de_json(json_dict, bot)
@ -79,13 +81,14 @@ class TestChatAdministratorRights:
assert admin_rights_dict['can_manage_chat'] == car.can_manage_chat assert admin_rights_dict['can_manage_chat'] == car.can_manage_chat
assert admin_rights_dict['is_anonymous'] == car.is_anonymous assert admin_rights_dict['is_anonymous'] == car.is_anonymous
assert admin_rights_dict['can_manage_video_chats'] == car.can_manage_video_chats assert admin_rights_dict['can_manage_video_chats'] == car.can_manage_video_chats
assert admin_rights_dict["can_manage_topics"] == car.can_manage_topics
def test_equality(self): def test_equality(self):
a = ChatAdministratorRights(True, False, False, False, False, False, False, False) a = ChatAdministratorRights(True, False, False, False, False, False, False, False, False)
b = ChatAdministratorRights(True, False, False, False, False, False, False, False) b = ChatAdministratorRights(True, False, False, False, False, False, False, False, False)
c = ChatAdministratorRights(False, False, False, False, False, False, False, False) c = ChatAdministratorRights(False, False, False, False, False, False, False, False, False)
d = ChatAdministratorRights(True, True, False, False, False, False, False, False) d = ChatAdministratorRights(True, True, False, False, False, False, False, False, False)
e = ChatAdministratorRights(True, True, False, False, False, False, False, False) e = ChatAdministratorRights(True, True, False, False, False, False, False, False, False)
assert a == b assert a == b
assert hash(a) == hash(b) assert hash(a) == hash(b)
@ -101,7 +104,7 @@ class TestChatAdministratorRights:
assert hash(d) == hash(e) assert hash(d) == hash(e)
def test_all_rights(self): def test_all_rights(self):
f = ChatAdministratorRights(True, True, True, True, True, True, True, True) f = ChatAdministratorRights(True, True, True, True, True, True, True, True, True)
t = ChatAdministratorRights.all_rights() t = ChatAdministratorRights.all_rights()
# if the dirs are the same, the attributes will all be there # if the dirs are the same, the attributes will all be there
assert dir(f) == dir(t) assert dir(f) == dir(t)
@ -116,7 +119,7 @@ class TestChatAdministratorRights:
assert f != t assert f != t
def test_no_rights(self): def test_no_rights(self):
f = ChatAdministratorRights(False, False, False, False, False, False, False, False) f = ChatAdministratorRights(False, False, False, False, False, False, False, False, False)
t = ChatAdministratorRights.no_rights() t = ChatAdministratorRights.no_rights()
# if the dirs are the same, the attributes will all be there # if the dirs are the same, the attributes will all be there
assert dir(f) == dir(t) assert dir(f) == dir(t)

View file

@ -120,6 +120,7 @@ class TestChatMember:
'can_add_web_page_previews': False, 'can_add_web_page_previews': False,
'can_manage_chat': True, 'can_manage_chat': True,
'can_manage_voice_chats': True, 'can_manage_voice_chats': True,
'can_manage_topics': True,
} }
chat_member_type = ChatMember.de_json(json_dict, bot) chat_member_type = ChatMember.de_json(json_dict, bot)
@ -221,6 +222,7 @@ class TestChatMember:
'can_add_web_page_previews': False, 'can_add_web_page_previews': False,
'can_manage_chat': True, 'can_manage_chat': True,
'can_manage_video_chats': True, 'can_manage_video_chats': True,
'can_manage_topics': True,
} }
assert type(cls.de_json(json_dict, bot)) is cls assert type(cls.de_json(json_dict, bot)) is cls

View file

@ -969,6 +969,21 @@ class TestFilters:
assert Filters.status_update.web_app_data(update) assert Filters.status_update.web_app_data(update)
update.message.web_app_data = None update.message.web_app_data = None
update.message.forum_topic_created = "topic"
assert Filters.status_update(update)
assert Filters.status_update.forum_topic_created(update)
update.message.forum_topic_created = None
update.message.forum_topic_closed = "topic"
assert Filters.status_update(update)
assert Filters.status_update.forum_topic_closed(update)
update.message.forum_topic_closed = None
update.message.forum_topic_reopened = "topic"
assert Filters.status_update(update)
assert Filters.status_update.forum_topic_reopened(update)
update.message.forum_topic_reopened = None
def test_filters_forwarded(self, update): def test_filters_forwarded(self, update):
assert not Filters.forwarded(update) assert not Filters.forwarded(update)
update.message.forward_date = datetime.datetime.utcnow() update.message.forward_date = datetime.datetime.utcnow()
@ -1762,6 +1777,11 @@ class TestFilters:
update.message.is_automatic_forward = True update.message.is_automatic_forward = True
assert Filters.is_automatic_forward(update) assert Filters.is_automatic_forward(update)
def test_filters_is_topic_message(self, update):
assert not Filters.is_topic_message(update)
update.message.is_topic_message = True
assert Filters.is_topic_message(update)
def test_filters_has_protected_content(self, update): def test_filters_has_protected_content(self, update):
assert not Filters.has_protected_content(update) assert not Filters.has_protected_content(update)
update.message.has_protected_content = True update.message.has_protected_content = True

331
tests/test_forum.py Normal file
View file

@ -0,0 +1,331 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2022
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import pytest
from telegram import ForumTopic, ForumTopicClosed, ForumTopicCreated, ForumTopicReopened, Sticker
TEST_MSG_TEXT = "Topics are forever"
TEST_TOPIC_ICON_COLOR = 0x6FB9F0
TEST_TOPIC_NAME = "Sad bot true: real stories"
@pytest.fixture(scope="module")
def emoji_id(bot):
emoji_sticker_list = bot.get_forum_topic_icon_stickers()
first_sticker = emoji_sticker_list[0]
return first_sticker.custom_emoji_id
@pytest.fixture
def forum_topic_object(forum_group_id, emoji_id):
return ForumTopic(
message_thread_id=forum_group_id,
name=TEST_TOPIC_NAME,
icon_color=TEST_TOPIC_ICON_COLOR,
icon_custom_emoji_id=emoji_id,
)
@pytest.fixture
def real_topic(bot, emoji_id, forum_group_id):
result = bot.create_forum_topic(
chat_id=forum_group_id,
name=TEST_TOPIC_NAME,
icon_color=TEST_TOPIC_ICON_COLOR,
icon_custom_emoji_id=emoji_id,
)
yield result
result = bot.delete_forum_topic(
chat_id=forum_group_id, message_thread_id=result.message_thread_id
)
assert result is True, "Topic was not deleted"
class TestForumTopic:
def test_slot_behaviour(self, mro_slots, forum_topic_object):
for attr in forum_topic_object.__slots__:
assert getattr(forum_topic_object, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(forum_topic_object)) == len(
set(mro_slots(forum_topic_object))
), "duplicate slot"
def test_expected_values(self, emoji_id, forum_group_id, forum_topic_object):
assert forum_topic_object.message_thread_id == forum_group_id
assert forum_topic_object.icon_color == TEST_TOPIC_ICON_COLOR
assert forum_topic_object.name == TEST_TOPIC_NAME
assert forum_topic_object.icon_custom_emoji_id == emoji_id
def test_de_json(self, bot, emoji_id, forum_group_id):
assert ForumTopic.de_json(None, bot=bot) is None
json_dict = {
"message_thread_id": forum_group_id,
"name": TEST_TOPIC_NAME,
"icon_color": TEST_TOPIC_ICON_COLOR,
"icon_custom_emoji_id": emoji_id,
}
topic = ForumTopic.de_json(json_dict, bot)
assert topic.message_thread_id == forum_group_id
assert topic.icon_color == TEST_TOPIC_ICON_COLOR
assert topic.name == TEST_TOPIC_NAME
assert topic.icon_custom_emoji_id == emoji_id
def test_to_dict(self, emoji_id, forum_group_id, forum_topic_object):
topic_dict = forum_topic_object.to_dict()
assert isinstance(topic_dict, dict)
assert topic_dict["message_thread_id"] == forum_group_id
assert topic_dict["name"] == TEST_TOPIC_NAME
assert topic_dict["icon_color"] == TEST_TOPIC_ICON_COLOR
assert topic_dict["icon_custom_emoji_id"] == emoji_id
def test_equality(self, emoji_id, forum_group_id):
a = ForumTopic(
message_thread_id=forum_group_id,
name=TEST_TOPIC_NAME,
icon_color=TEST_TOPIC_ICON_COLOR,
)
b = ForumTopic(
message_thread_id=forum_group_id,
name=TEST_TOPIC_NAME,
icon_color=TEST_TOPIC_ICON_COLOR,
icon_custom_emoji_id=emoji_id,
)
c = ForumTopic(
message_thread_id=forum_group_id,
name=f"{TEST_TOPIC_NAME}!",
icon_color=TEST_TOPIC_ICON_COLOR,
)
d = ForumTopic(
message_thread_id=forum_group_id + 1,
name=TEST_TOPIC_NAME,
icon_color=TEST_TOPIC_ICON_COLOR,
)
e = ForumTopic(
message_thread_id=forum_group_id,
name=TEST_TOPIC_NAME,
icon_color=0xFFD67E,
)
assert a == b
assert hash(a) == hash(b)
assert a != c
assert hash(a) != hash(c)
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)
@pytest.mark.flaky(3, 1)
def test_create_forum_topic(self, real_topic):
result = real_topic
assert isinstance(result, ForumTopic)
assert result.name == TEST_TOPIC_NAME
assert result.message_thread_id
assert isinstance(result.icon_color, int)
assert isinstance(result.icon_custom_emoji_id, str)
def test_create_forum_topic_with_only_required_args(self, bot, forum_group_id):
result = bot.create_forum_topic(chat_id=forum_group_id, name=TEST_TOPIC_NAME)
assert isinstance(result, ForumTopic)
assert result.name == TEST_TOPIC_NAME
assert result.message_thread_id
assert isinstance(result.icon_color, int) # color is still there though it was not passed
assert result.icon_custom_emoji_id is None
result = bot.delete_forum_topic(
chat_id=forum_group_id, message_thread_id=result.message_thread_id
)
assert result is True, "Failed to delete forum topic"
@pytest.mark.flaky(3, 1)
def test_get_forum_topic_icon_stickers(self, bot):
emoji_sticker_list = bot.get_forum_topic_icon_stickers()
first_sticker = emoji_sticker_list[0]
assert first_sticker.emoji == "📰"
assert first_sticker.height == 512
assert first_sticker.width == 512
assert first_sticker.is_animated
assert not first_sticker.is_video
assert first_sticker.set_name == "Topics"
assert first_sticker.type == Sticker.CUSTOM_EMOJI
assert first_sticker.thumb.width == 128
assert first_sticker.thumb.height == 128
# The following data of first item returned has changed in the past already,
# so check sizes loosely and ID's only by length of string
assert first_sticker.thumb.file_size in range(2000, 7000)
assert first_sticker.file_size in range(20000, 70000)
assert len(first_sticker.custom_emoji_id) == 19
assert len(first_sticker.thumb.file_unique_id) == 16
assert len(first_sticker.file_unique_id) == 15
def test_edit_forum_topic(self, emoji_id, forum_group_id, bot, real_topic):
result = bot.edit_forum_topic(
chat_id=forum_group_id,
message_thread_id=real_topic.message_thread_id,
name=f"{TEST_TOPIC_NAME}_EDITED",
icon_custom_emoji_id=emoji_id,
)
assert result is True, "Failed to edit forum topic"
# no way of checking the edited name, just the boolean result
@pytest.mark.flaky(3, 1)
def test_send_message_to_topic(self, bot, forum_group_id, real_topic):
message_thread_id = real_topic.message_thread_id
message = bot.send_message(
chat_id=forum_group_id, text=TEST_MSG_TEXT, message_thread_id=message_thread_id
)
assert message.text == TEST_MSG_TEXT
assert message.is_topic_message is True
assert message.message_thread_id == message_thread_id
def test_close_and_reopen_forum_topic(self, bot, forum_group_id, real_topic):
message_thread_id = real_topic.message_thread_id
result = bot.close_forum_topic(
chat_id=forum_group_id,
message_thread_id=message_thread_id,
)
assert result is True, "Failed to close forum topic"
# bot will still be able to send a message to a closed topic, so can't test anything like
# the inability to post to the topic
result = bot.reopen_forum_topic(
chat_id=forum_group_id,
message_thread_id=message_thread_id,
)
assert result is True, "Failed to reopen forum topic"
@pytest.mark.xfail(reason="Can fail due to race conditions in GH actions CI")
def test_unpin_all_forum_topic_messages(self, bot, forum_group_id, real_topic):
message_thread_id = real_topic.message_thread_id
msgs = [
(
bot.send_message(
chat_id=forum_group_id, text=TEST_MSG_TEXT, message_thread_id=message_thread_id
)
).pin()
for _ in range(2)
]
assert all(msgs) is True, "Message(s) were not pinned"
# We need 2 or more pinned msgs for this to work, else we get Chat_not_modified error
result = bot.unpin_all_forum_topic_messages(
chat_id=forum_group_id, message_thread_id=message_thread_id
)
assert result is True, "Failed to unpin all the messages in forum topic"
@pytest.fixture
def topic_created():
return ForumTopicCreated(name=TEST_TOPIC_NAME, icon_color=TEST_TOPIC_ICON_COLOR)
class TestForumTopicCreated:
def test_slot_behaviour(self, topic_created, mro_slots):
for attr in topic_created.__slots__:
assert getattr(topic_created, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(topic_created)) == len(
set(mro_slots(topic_created))
), "duplicate slot"
def test_expected_values(self, topic_created):
assert topic_created.icon_color == TEST_TOPIC_ICON_COLOR
assert topic_created.name == TEST_TOPIC_NAME
def test_de_json(self, bot):
assert ForumTopicCreated.de_json(None, bot=bot) is None
json_dict = {"icon_color": TEST_TOPIC_ICON_COLOR, "name": TEST_TOPIC_NAME}
action = ForumTopicCreated.de_json(json_dict, bot)
assert action.icon_color == TEST_TOPIC_ICON_COLOR
assert action.name == TEST_TOPIC_NAME
def test_to_dict(self, topic_created):
action_dict = topic_created.to_dict()
assert isinstance(action_dict, dict)
assert action_dict["name"] == TEST_TOPIC_NAME
assert action_dict["icon_color"] == TEST_TOPIC_ICON_COLOR
def test_equality(self, emoji_id):
a = ForumTopicCreated(name=TEST_TOPIC_NAME, icon_color=TEST_TOPIC_ICON_COLOR)
b = ForumTopicCreated(
name=TEST_TOPIC_NAME,
icon_color=TEST_TOPIC_ICON_COLOR,
icon_custom_emoji_id=emoji_id,
)
c = ForumTopicCreated(name=f"{TEST_TOPIC_NAME}!", icon_color=TEST_TOPIC_ICON_COLOR)
d = ForumTopicCreated(name=TEST_TOPIC_NAME, icon_color=0xFFD67E)
assert a == b
assert hash(a) == hash(b)
assert a != c
assert hash(a) != hash(c)
assert a != d
assert hash(a) != hash(d)
class TestForumTopicClosed:
def test_slot_behaviour(self, mro_slots):
action = ForumTopicClosed()
for attr in action.__slots__:
assert getattr(action, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot"
def test_de_json(self):
action = ForumTopicClosed.de_json({}, None)
assert isinstance(action, ForumTopicClosed)
def test_to_dict(self):
action = ForumTopicClosed()
action_dict = action.to_dict()
assert action_dict == {}
class TestForumTopicReopened:
def test_slot_behaviour(self, mro_slots):
action = ForumTopicReopened()
for attr in action.__slots__:
assert getattr(action, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot"
def test_de_json(self):
action = ForumTopicReopened.de_json({}, None)
assert isinstance(action, ForumTopicReopened)
def test_to_dict(self):
action = ForumTopicReopened()
action_dict = action.to_dict()
assert action_dict == {}

View file

@ -43,6 +43,9 @@ from .test_audio import audio, audio_file # noqa: F401
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
from .test_document import document, document_file # noqa: F401 from .test_document import document, document_file # noqa: F401
# noinspection PyUnresolvedReferences
from .test_forum import emoji_id, real_topic # noqa: F401
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
from .test_photo import _photo, photo_file, photo, thumb # noqa: F401 from .test_photo import _photo, photo_file, photo, thumb # noqa: F401
@ -458,6 +461,19 @@ class TestSendMediaGroup:
mes.caption_entities == [MessageEntity(MessageEntity.BOLD, 0, 5)] for mes in messages mes.caption_entities == [MessageEntity(MessageEntity.BOLD, 0, 5)] for mes in messages
) )
def test_send_media_group_with_message_thread_id(
self, bot, real_topic, forum_group_id, media_group # noqa: F811
):
messages = bot.send_media_group(
forum_group_id,
media_group,
message_thread_id=real_topic.message_thread_id,
)
assert isinstance(messages, list)
assert len(messages) == 3
assert all(isinstance(mes, Message) for mes in messages)
assert all(i.message_thread_id == real_topic.message_thread_id for i in messages)
@flaky(3, 1) @flaky(3, 1)
def test_send_media_group_all_args(self, bot, chat_id, media_group): def test_send_media_group_all_args(self, bot, chat_id, media_group):
m1 = bot.send_message(chat_id, text="test") m1 = bot.send_message(chat_id, text="test")

View file

@ -196,6 +196,7 @@ def message(bot):
) )
}, },
{'web_app_data': WebAppData('some_data', 'some_button_text')}, {'web_app_data': WebAppData('some_data', 'some_button_text')},
{"message_thread_id": 123},
], ],
ids=[ ids=[
'forwarded_user', 'forwarded_user',
@ -252,6 +253,7 @@ def message(bot):
'is_automatic_forward', 'is_automatic_forward',
'has_protected_content', 'has_protected_content',
'web_app_data', 'web_app_data',
'message_thread_id',
], ],
) )
def message_params(bot, request): def message_params(bot, request):
@ -1634,6 +1636,122 @@ class TestMessage:
finally: finally:
message.bot.defaults = None message.bot.defaults = None
def test_edit_forum_topic(self, monkeypatch, message):
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 check_shortcut_call(
message.edit_forum_topic,
message.bot,
"edit_forum_topic",
shortcut_kwargs=["chat_id", "message_thread_id"],
)
assert check_defaults_handling(message.edit_forum_topic, message.bot)
monkeypatch.setattr(message.bot, "edit_forum_topic", make_assertion)
assert message.edit_forum_topic(name="New Name", icon_custom_emoji_id="12345")
def test_close_forum_topic(self, monkeypatch, message):
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 check_shortcut_call(
message.close_forum_topic,
message.bot,
"close_forum_topic",
shortcut_kwargs=["chat_id", "message_thread_id"],
)
assert check_defaults_handling(message.close_forum_topic, message.bot)
monkeypatch.setattr(message.bot, "close_forum_topic", make_assertion)
assert message.close_forum_topic()
def test_reopen_forum_topic(self, monkeypatch, message):
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 check_shortcut_call(
message.reopen_forum_topic,
message.bot,
"reopen_forum_topic",
shortcut_kwargs=["chat_id", "message_thread_id"],
)
assert check_defaults_handling(message.reopen_forum_topic, message.bot)
monkeypatch.setattr(message.bot, "reopen_forum_topic", make_assertion)
assert message.reopen_forum_topic()
def test_delete_forum_topic(self, monkeypatch, message):
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 check_shortcut_call(
message.delete_forum_topic,
message.bot,
"delete_forum_topic",
shortcut_kwargs=["chat_id", "message_thread_id"],
)
assert check_defaults_handling(message.delete_forum_topic, message.bot)
monkeypatch.setattr(message.bot, "delete_forum_topic", make_assertion)
assert message.delete_forum_topic()
def test_unpin_all_forum_topic_messages(self, monkeypatch, message):
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 check_shortcut_call(
message.unpin_all_forum_topic_messages,
message.bot,
"unpin_all_forum_topic_messages",
shortcut_kwargs=["chat_id", "message_thread_id"],
)
assert check_defaults_handling(message.unpin_all_forum_topic_messages, message.bot)
monkeypatch.setattr(message.bot, "unpin_all_forum_topic_messages", make_assertion)
assert message.unpin_all_forum_topic_messages()
def test_equality(self): def test_equality(self):
id_ = 1 id_ = 1
a = Message( a = Message(

View file

@ -174,6 +174,7 @@ def check_object(h4):
'is_anonymous', 'is_anonymous',
'is_member', 'is_member',
'until_date', 'until_date',
'can_manage_topics',
} }
if name == 'BotCommandScope': if name == 'BotCommandScope':
ignored |= {'type'} # attributes common to all subclasses ignored |= {'type'} # attributes common to all subclasses