diff --git a/README.md b/README.md
index b8c4a6c84..5eb412ce7 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
A simple, but extensible Python implementation for the Telegram Bot API.
Both synchronous and asynchronous.
-## Supported Bot API version: 6.3!
+##
Supported Bot API version: 6.4!
diff --git a/telebot/__init__.py b/telebot/__init__.py
index 6a4dd7ba1..ef8ab7146 100644
--- a/telebot/__init__.py
+++ b/telebot/__init__.py
@@ -1763,7 +1763,8 @@ def send_photo(
allow_sending_without_reply: Optional[bool]=None,
reply_markup: Optional[REPLY_MARKUP_TYPES]=None,
timeout: Optional[int]=None,
- message_thread_id: Optional[int]=None) -> types.Message:
+ message_thread_id: Optional[int]=None,
+ has_spoiler: Optional[bool]=None) -> types.Message:
"""
Use this method to send photos. On success, the sent Message is returned.
@@ -1808,6 +1809,9 @@ def send_photo(
:param message_thread_id: Identifier of a message thread, in which the message will be sent
:type message_thread_id: :obj:`int`
+
+ :param has_spoiler: Pass True, if the photo should be sent as a spoiler
+ :type has_spoiler: :obj:`bool`
:return: On success, the sent Message is returned.
:rtype: :class:`telebot.types.Message`
@@ -1821,7 +1825,7 @@ def send_photo(
apihelper.send_photo(
self.token, chat_id, photo, caption, reply_to_message_id, reply_markup,
parse_mode, disable_notification, timeout, caption_entities,
- allow_sending_without_reply, protect_content, message_thread_id))
+ allow_sending_without_reply, protect_content, message_thread_id, has_spoiler))
# TODO: Rewrite this method like in API.
def send_audio(
@@ -2171,7 +2175,8 @@ def send_video(
reply_markup: Optional[REPLY_MARKUP_TYPES]=None,
timeout: Optional[int]=None,
data: Optional[Union[Any, str]]=None,
- message_thread_id: Optional[int]=None) -> types.Message:
+ message_thread_id: Optional[int]=None,
+ has_spoiler: Optional[bool]=None) -> types.Message:
"""
Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document).
@@ -2233,6 +2238,9 @@ def send_video(
:param message_thread_id: Identifier of a message thread, in which the video will be sent
:type message_thread_id: :obj:`int`
+ :param has_spoiler: Pass True, if the video should be sent as a spoiler
+ :type has_spoiler: :obj:`bool`
+
:return: On success, the sent Message is returned.
:rtype: :class:`telebot.types.Message`
"""
@@ -2249,7 +2257,7 @@ def send_video(
apihelper.send_video(
self.token, chat_id, video, duration, caption, reply_to_message_id, reply_markup,
parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height,
- caption_entities, allow_sending_without_reply, protect_content, message_thread_id))
+ caption_entities, allow_sending_without_reply, protect_content, message_thread_id, has_spoiler))
def send_animation(
self, chat_id: Union[int, str], animation: Union[Any, str],
@@ -2266,7 +2274,8 @@ def send_animation(
allow_sending_without_reply: Optional[bool]=None,
reply_markup: Optional[REPLY_MARKUP_TYPES]=None,
timeout: Optional[int]=None,
- message_thread_id: Optional[int]=None) -> types.Message:
+ message_thread_id: Optional[int]=None,
+ has_spoiler: Optional[bool]=None) -> types.Message:
"""
Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound).
On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future.
@@ -2327,6 +2336,9 @@ def send_animation(
:param message_thread_id: Identifier of a message thread, in which the video will be sent
:type message_thread_id: :obj:`int`
+ :param has_spoiler: Pass True, if the animation should be sent as a spoiler
+ :type has_spoiler: :obj:`bool`
+
:return: On success, the sent Message is returned.
:rtype: :class:`telebot.types.Message`
"""
@@ -2339,7 +2351,7 @@ def send_animation(
apihelper.send_animation(
self.token, chat_id, animation, duration, caption, reply_to_message_id,
reply_markup, parse_mode, disable_notification, timeout, thumb,
- caption_entities, allow_sending_without_reply, protect_content, width, height, message_thread_id))
+ caption_entities, allow_sending_without_reply, protect_content, width, height, message_thread_id, has_spoiler))
# TODO: Rewrite this method like in API.
def send_video_note(
@@ -2794,7 +2806,7 @@ def send_contact(
allow_sending_without_reply, protect_content, message_thread_id))
def send_chat_action(
- self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None) -> bool:
+ self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None, message_thread_id: Optional[int]=None) -> bool:
"""
Use this method when you need to tell the user that something is happening on the bot's side.
The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status).
@@ -2817,10 +2829,13 @@ def send_chat_action(
:param timeout: Timeout in seconds for the request.
:type timeout: :obj:`int`
+ :param message_thread_id: The thread identifier of a message from which the reply will be sent(supergroups only)
+ :type message_thread_id: :obj:`int`
+
:return: Returns True on success.
:rtype: :obj:`bool`
"""
- return apihelper.send_chat_action(self.token, chat_id, action, timeout)
+ return apihelper.send_chat_action(self.token, chat_id, action, timeout, message_thread_id)
@util.deprecated(deprecation_text="Use ban_chat_member instead")
def kick_chat_member(
@@ -4636,8 +4651,8 @@ def create_forum_topic(self,
def edit_forum_topic(
self, chat_id: Union[int, str],
- message_thread_id: int, name: str,
- icon_custom_emoji_id: str,
+ message_thread_id: int, name: Optional[str]=None,
+ icon_custom_emoji_id: Optional[str]=None
) -> bool:
"""
Use this method to edit name and icon of a topic in a forum supergroup chat. The bot must be an
@@ -4652,10 +4667,13 @@ def edit_forum_topic(
:param message_thread_id: Identifier of the topic to edit
:type message_thread_id: :obj:`int`
- :param name: New name of the topic, 1-128 characters
+ :param name: Optional, New name of the topic, 1-128 characters. If not specififed or empty,
+ the current name of the topic will be kept
:type name: :obj:`str`
- :param icon_custom_emoji_id: New custom emoji for the topic icon. Must be an emoji of type “tgs” and must be exactly 1 character long
+ :param icon_custom_emoji_id: Optional, New unique identifier of the custom emoji shown as the topic icon.
+ Use getForumTopicIconStickers to get all allowed custom emoji identifiers. Pass an empty string to remove the
+ icon. If not specified, the current icon will be kept
:type icon_custom_emoji_id: :obj:`str`
:return: On success, True is returned.
@@ -4739,6 +4757,75 @@ def unpin_all_forum_topic_messages(self, chat_id: Union[str, int], message_threa
"""
return apihelper.unpin_all_forum_topic_messages(self.token, chat_id, message_thread_id)
+ def edit_general_forum_topic(self, chat_id: Union[int, str], name: str) -> bool:
+ """
+ Use this method to edit the name of the 'General' topic in a forum supergroup chat.
+ The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights.
+ Returns True on success.
+
+ Telegram documentation: https://core.telegram.org/bots/api#editgeneralforumtopic
+
+ :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)
+ :type chat_id: :obj:`int` or :obj:`str`
+
+ :param name: New topic name, 1-128 characters
+ :type name: :obj:`str`
+ """
+
+ return apihelper.edit_general_forum_topic(self.token, chat_id, name)
+
+ def close_general_forum_topic(self, chat_id: Union[int, str]) -> bool:
+ """
+ Use this method to close the 'General' topic in a forum supergroup chat.
+ The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights.
+ Returns True on success.
+
+ Telegram documentation: https://core.telegram.org/bots/api#closegeneralforumtopic
+
+ :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)
+ :type chat_id: :obj:`int` or :obj:`str`
+ """
+ return apihelper.close_general_forum_topic(self.token, chat_id)
+
+ def reopen_general_forum_topic(self, chat_id: Union[int, str]) -> bool:
+ """
+ Use this method to reopen the 'General' topic in a forum supergroup chat.
+ The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights.
+ Returns True on success.
+
+ Telegram documentation: https://core.telegram.org/bots/api#reopengeneralforumtopic
+
+ :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)
+ :type chat_id: :obj:`int` or :obj:`str`
+ """
+ return apihelper.reopen_general_forum_topic(self.token, chat_id)
+
+ def hide_general_forum_topic(self, chat_id: Union[int, str]) -> bool:
+ """
+ Use this method to hide the 'General' topic in a forum supergroup chat.
+ The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights.
+ Returns True on success.
+
+ Telegram documentation: https://core.telegram.org/bots/api#hidegeneralforumtopic
+
+ :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)
+ :type chat_id: :obj:`int` or :obj:`str`
+ """
+ return apihelper.hide_general_forum_topic(self.token, chat_id)
+
+ def unhide_general_forum_topic(self, chat_id: Union[int, str]) -> bool:
+ """
+ Use this method to unhide the 'General' topic in a forum supergroup chat.
+ The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights.
+ Returns True on success.
+
+ Telegram documentation: https://core.telegram.org/bots/api#unhidegeneralforumtopic
+
+ :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)
+ :type chat_id: :obj:`int` or :obj:`str`
+ """
+ return apihelper.unhide_general_forum_topic(self.token, chat_id)
+
def get_forum_topic_icon_stickers(self) -> List[types.Sticker]:
"""
Use this method to get custom emoji stickers, which can be used as a forum topic icon by any user.
diff --git a/telebot/apihelper.py b/telebot/apihelper.py
index b25e0365e..1086f54e9 100644
--- a/telebot/apihelper.py
+++ b/telebot/apihelper.py
@@ -459,7 +459,7 @@ def send_photo(
caption=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None, timeout=None,
caption_entities=None, allow_sending_without_reply=None, protect_content=None,
- message_thread_id=None):
+ message_thread_id=None, has_spoiler=None):
method_url = r'sendPhoto'
payload = {'chat_id': chat_id}
files = None
@@ -489,6 +489,8 @@ def send_photo(
payload['protect_content'] = protect_content
if message_thread_id is not None:
payload['message_thread_id'] = message_thread_id
+ if has_spoiler is not None:
+ payload['has_spoiler'] = has_spoiler
return _make_request(token, method_url, params=payload, files=files, method='post')
@@ -655,18 +657,20 @@ def send_contact(
return _make_request(token, method_url, params=payload)
-def send_chat_action(token, chat_id, action, timeout=None):
+def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=None):
method_url = r'sendChatAction'
payload = {'chat_id': chat_id, 'action': action}
if timeout:
payload['timeout'] = timeout
+ if message_thread_id is not None:
+ payload['message_thread_id'] = message_thread_id
return _make_request(token, method_url, params=payload)
def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None,
thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None, protect_content=None,
- message_thread_id=None):
+ message_thread_id=None, has_spoiler=None):
method_url = r'sendVideo'
payload = {'chat_id': chat_id}
files = None
@@ -710,13 +714,16 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
payload['protect_content'] = protect_content
if message_thread_id:
payload['message_thread_id'] = message_thread_id
+ if has_spoiler is not None:
+ payload['has_spoiler'] = has_spoiler
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_animation(
token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None, timeout=None, thumb=None, caption_entities=None,
- allow_sending_without_reply=None, protect_content=None, width=None, height=None, message_thread_id=None):
+ allow_sending_without_reply=None, protect_content=None, width=None, height=None, message_thread_id=None,
+ has_spoiler=None):
method_url = r'sendAnimation'
payload = {'chat_id': chat_id}
files = None
@@ -758,6 +765,8 @@ def send_animation(
payload['height'] = height
if message_thread_id:
payload['message_thread_id'] = message_thread_id
+ if has_spoiler is not None:
+ payload['has_spoiler'] = has_spoiler
return _make_request(token, method_url, params=payload, files=files, method='post')
@@ -1766,9 +1775,13 @@ def create_forum_topic(token, chat_id, name, icon_color=None, icon_custom_emoji_
payload['icon_custom_emoji_id'] = icon_custom_emoji_id
return _make_request(token, method_url, params=payload)
-def edit_forum_topic(token, chat_id, message_thread_id, name, icon_custom_emoji_id):
+def edit_forum_topic(token, chat_id, message_thread_id, name=None, icon_custom_emoji_id=None):
method_url = r'editForumTopic'
- payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id, 'name': name, 'icon_custom_emoji_id': icon_custom_emoji_id}
+ payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id}
+ if name is not None:
+ payload['name'] = name
+ if icon_custom_emoji_id is not None:
+ payload['icon_custom_emoji_id'] = icon_custom_emoji_id
return _make_request(token, method_url, params=payload)
def close_forum_topic(token, chat_id, message_thread_id):
@@ -1802,6 +1815,31 @@ def stop_poll(token, chat_id, message_id, reply_markup=None):
payload['reply_markup'] = _convert_markup(reply_markup)
return _make_request(token, method_url, params=payload)
+def edit_general_forum_topic(token, chat_id, name):
+ method_url = r'editGeneralForumTopic'
+ payload = {'chat_id': chat_id, 'name': name}
+ return _make_request(token, method_url, params=payload)
+
+def close_general_forum_topic(token, chat_id):
+ method_url = r'closeGeneralForumTopic'
+ payload = {'chat_id': chat_id}
+ return _make_request(token, method_url, params=payload)
+
+def reopen_general_forum_topic(token, chat_id):
+ method_url = r'reopenGeneralForumTopic'
+ payload = {'chat_id': chat_id}
+ return _make_request(token, method_url, params=payload)
+
+def hide_general_forum_topic(token, chat_id):
+ method_url = r'hideGeneralForumTopic'
+ payload = {'chat_id': chat_id}
+ return _make_request(token, method_url, params=payload)
+
+def unhide_general_forum_topic(token, chat_id):
+ method_url = r'unhideGeneralForumTopic'
+ payload = {'chat_id': chat_id}
+ return _make_request(token, method_url, params=payload)
+
def _convert_list_json_serializable(results):
ret = ''
diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py
index e8249fc86..6e3ec9ad7 100644
--- a/telebot/async_telebot.py
+++ b/telebot/async_telebot.py
@@ -2627,7 +2627,8 @@ async def send_photo(
allow_sending_without_reply: Optional[bool]=None,
reply_markup: Optional[REPLY_MARKUP_TYPES]=None,
timeout: Optional[int]=None,
- message_thread_id: Optional[int]=None) -> types.Message:
+ message_thread_id: Optional[int]=None,
+ has_spoiler: Optional[bool]=None) -> types.Message:
"""
Use this method to send photos. On success, the sent Message is returned.
@@ -2672,6 +2673,9 @@ async def send_photo(
:param message_thread_id: Identifier of a message thread, in which the message will be sent
:type message_thread_id: :obj:`int`
+
+ :param has_spoiler: Pass True, if the photo should be sent as a spoiler
+ :type has_spoiler: :obj:`bool`
:return: On success, the sent Message is returned.
:rtype: :class:`telebot.types.Message`
@@ -2685,7 +2689,7 @@ async def send_photo(
await asyncio_helper.send_photo(
self.token, chat_id, photo, caption, reply_to_message_id, reply_markup,
parse_mode, disable_notification, timeout, caption_entities,
- allow_sending_without_reply, protect_content, message_thread_id))
+ allow_sending_without_reply, protect_content, message_thread_id, has_spoiler))
async def send_audio(
self, chat_id: Union[int, str], audio: Union[Any, str],
@@ -3031,7 +3035,8 @@ async def send_video(
reply_markup: Optional[REPLY_MARKUP_TYPES]=None,
timeout: Optional[int]=None,
data: Optional[Union[Any, str]]=None,
- message_thread_id: Optional[int]=None) -> types.Message:
+ message_thread_id: Optional[int]=None,
+ has_spoiler: Optional[bool]=None) -> types.Message:
"""
Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document).
@@ -3093,6 +3098,9 @@ async def send_video(
:param message_thread_id: Identifier of a message thread, in which the video will be sent
:type message_thread_id: :obj:`int`
+ :param has_spoiler: Pass True, if the video should be sent as a spoiler
+ :type has_spoiler: :obj:`bool`
+
:return: On success, the sent Message is returned.
:rtype: :class:`telebot.types.Message`
"""
@@ -3110,7 +3118,7 @@ async def send_video(
await asyncio_helper.send_video(
self.token, chat_id, video, duration, caption, reply_to_message_id, reply_markup,
parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height,
- caption_entities, allow_sending_without_reply, protect_content, message_thread_id))
+ caption_entities, allow_sending_without_reply, protect_content, message_thread_id, has_spoiler))
async def send_animation(
self, chat_id: Union[int, str], animation: Union[Any, str],
@@ -3127,7 +3135,8 @@ async def send_animation(
allow_sending_without_reply: Optional[bool]=None,
reply_markup: Optional[REPLY_MARKUP_TYPES]=None,
timeout: Optional[int]=None,
- message_thread_id: Optional[int]=None) -> types.Message:
+ message_thread_id: Optional[int]=None,
+ has_spoiler: Optional[bool]=None) -> types.Message:
"""
Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound).
On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future.
@@ -3188,6 +3197,9 @@ async def send_animation(
:param message_thread_id: Identifier of a message thread, in which the video will be sent
:type message_thread_id: :obj:`int`
+ :param has_spoiler: Pass True, if the animation should be sent as a spoiler
+ :type has_spoiler: :obj:`bool`
+
:return: On success, the sent Message is returned.
:rtype: :class:`telebot.types.Message`
"""
@@ -3200,7 +3212,7 @@ async def send_animation(
await asyncio_helper.send_animation(
self.token, chat_id, animation, duration, caption, reply_to_message_id,
reply_markup, parse_mode, disable_notification, timeout, thumb,
- caption_entities, allow_sending_without_reply, width, height, protect_content, message_thread_id))
+ caption_entities, allow_sending_without_reply, width, height, protect_content, message_thread_id, has_spoiler))
async def send_video_note(
self, chat_id: Union[int, str], data: Union[Any, str],
@@ -3652,7 +3664,7 @@ async def send_contact(
)
async def send_chat_action(
- self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None) -> bool:
+ self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None, message_thread_id: Optional[int]=None) -> bool:
"""
Use this method when you need to tell the user that something is happening on the bot's side.
The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status).
@@ -3675,10 +3687,13 @@ async def send_chat_action(
:param timeout: Timeout in seconds for the request.
:type timeout: :obj:`int`
+ :param message_thread_id: The thread to which the message will be sent(supergroups only)
+ :type message_thread_id: :obj:`int`
+
:return: Returns True on success.
:rtype: :obj:`bool`
"""
- return await asyncio_helper.send_chat_action(self.token, chat_id, action, timeout)
+ return await asyncio_helper.send_chat_action(self.token, chat_id, action, timeout, message_thread_id)
async def kick_chat_member(
self, chat_id: Union[int, str], user_id: int,
@@ -5502,8 +5517,8 @@ async def create_forum_topic(self,
async def edit_forum_topic(
self, chat_id: Union[int, str],
- message_thread_id: int, name: str,
- icon_custom_emoji_id: str,
+ message_thread_id: int, name: Optional[str]=None,
+ icon_custom_emoji_id: Optional[str]=None
) -> bool:
"""
Use this method to edit name and icon of a topic in a forum supergroup chat. The bot must be an
@@ -5518,10 +5533,13 @@ async def edit_forum_topic(
:param message_thread_id: Identifier of the topic to edit
:type message_thread_id: :obj:`int`
- :param name: New name of the topic, 1-128 characters
+ :param name: Optional, New name of the topic, 1-128 characters. If not specififed or empty,
+ the current name of the topic will be kept
:type name: :obj:`str`
- :param icon_custom_emoji_id: New custom emoji for the topic icon. Must be an emoji of type “tgs” and must be exactly 1 character long
+ :param icon_custom_emoji_id: Optional, New unique identifier of the custom emoji shown as the topic icon.
+ Use getForumTopicIconStickers to get all allowed custom emoji identifiers. Pass an empty string to remove the
+ icon. If not specified, the current icon will be kept
:type icon_custom_emoji_id: :obj:`str`
:return: On success, True is returned.
@@ -5605,6 +5623,75 @@ async def unpin_all_forum_topic_messages(self, chat_id: Union[str, int], message
"""
return await asyncio_helper.unpin_all_forum_topic_messages(self.token, chat_id, message_thread_id)
+ async def edit_general_forum_topic(self, chat_id: Union[int, str], name: str) -> bool:
+ """
+ Use this method to edit the name of the 'General' topic in a forum supergroup chat.
+ The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights.
+ Returns True on success.
+
+ Telegram documentation: https://core.telegram.org/bots/api#editgeneralforumtopic
+
+ :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)
+ :type chat_id: :obj:`int` or :obj:`str`
+
+ :param name: New topic name, 1-128 characters
+ :type name: :obj:`str`
+ """
+
+ return await asyncio_helper.edit_general_forum_topic(self.token, chat_id, name)
+
+ async def close_general_forum_topic(self, chat_id: Union[int, str]) -> bool:
+ """
+ Use this method to close the 'General' topic in a forum supergroup chat.
+ The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights.
+ Returns True on success.
+
+ Telegram documentation: https://core.telegram.org/bots/api#closegeneralforumtopic
+
+ :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)
+ :type chat_id: :obj:`int` or :obj:`str`
+ """
+ return await asyncio_helper.close_general_forum_topic(self.token, chat_id)
+
+ async def reopen_general_forum_topic(self, chat_id: Union[int, str]) -> bool:
+ """
+ Use this method to reopen the 'General' topic in a forum supergroup chat.
+ The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights.
+ Returns True on success.
+
+ Telegram documentation: https://core.telegram.org/bots/api#reopengeneralforumtopic
+
+ :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)
+ :type chat_id: :obj:`int` or :obj:`str`
+ """
+ return await asyncio_helper.reopen_general_forum_topic(self.token, chat_id)
+
+ async def hide_general_forum_topic(self, chat_id: Union[int, str]) -> bool:
+ """
+ Use this method to hide the 'General' topic in a forum supergroup chat.
+ The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights.
+ Returns True on success.
+
+ Telegram documentation: https://core.telegram.org/bots/api#hidegeneralforumtopic
+
+ :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)
+ :type chat_id: :obj:`int` or :obj:`str`
+ """
+ return await asyncio_helper.hide_general_forum_topic(self.token, chat_id)
+
+ async def unhide_general_forum_topic(self, chat_id: Union[int, str]) -> bool:
+ """
+ Use this method to unhide the 'General' topic in a forum supergroup chat.
+ The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights.
+ Returns True on success.
+
+ Telegram documentation: https://core.telegram.org/bots/api#unhidegeneralforumtopic
+
+ :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)
+ :type chat_id: :obj:`int` or :obj:`str`
+ """
+ return await asyncio_helper.unhide_general_forum_topic(self.token, chat_id)
+
async def get_forum_topic_icon_stickers(self) -> List[types.Sticker]:
"""
Use this method to get custom emoji stickers, which can be used as a forum topic icon by any user.
diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py
index 9f5b59a71..437188566 100644
--- a/telebot/asyncio_helper.py
+++ b/telebot/asyncio_helper.py
@@ -453,7 +453,7 @@ async def send_photo(
caption=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None, timeout=None,
caption_entities=None, allow_sending_without_reply=None, protect_content=None,
- message_thread_id=None):
+ message_thread_id=None, has_spoiler=None):
method_url = r'sendPhoto'
payload = {'chat_id': chat_id}
files = None
@@ -483,6 +483,8 @@ async def send_photo(
payload['protect_content'] = protect_content
if message_thread_id:
payload['message_thread_id'] = message_thread_id
+ if has_spoiler is not None:
+ payload['has_spoiler'] = has_spoiler
return await _process_request(token, method_url, params=payload, files=files, method='post')
@@ -647,18 +649,20 @@ async def send_contact(
return await _process_request(token, method_url, params=payload)
-async def send_chat_action(token, chat_id, action, timeout=None):
+async def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=None):
method_url = r'sendChatAction'
payload = {'chat_id': chat_id, 'action': action}
if timeout:
payload['timeout'] = timeout
+ if message_thread_id:
+ payload['message_thread_id'] = message_thread_id
return await _process_request(token, method_url, params=payload)
async def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None,
thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None,
- protect_content=None, message_thread_id=None):
+ protect_content=None, message_thread_id=None, has_spoiler=None):
method_url = r'sendVideo'
payload = {'chat_id': chat_id}
files = None
@@ -702,13 +706,16 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_to
payload['protect_content'] = protect_content
if message_thread_id:
payload['message_thread_id'] = message_thread_id
+ if has_spoiler is not None:
+ payload['has_spoiler'] = has_spoiler
return await _process_request(token, method_url, params=payload, files=files, method='post')
async def send_animation(
token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None, timeout=None, thumb=None, caption_entities=None,
- allow_sending_without_reply=None, width=None, height=None, protect_content=None, message_thread_id=None):
+ allow_sending_without_reply=None, width=None, height=None, protect_content=None, message_thread_id=None,
+ has_spoiler=None):
method_url = r'sendAnimation'
payload = {'chat_id': chat_id}
files = None
@@ -750,6 +757,8 @@ async def send_animation(
payload['protect_content'] = protect_content
if message_thread_id:
payload['message_thread_id'] = message_thread_id
+ if has_spoiler is not None:
+ payload['has_spoiler'] = has_spoiler
return await _process_request(token, method_url, params=payload, files=files, method='post')
@@ -1757,9 +1766,13 @@ async def create_forum_topic(token, chat_id, name, icon_color=None, icon_custom_
payload['icon_custom_emoji_id'] = icon_custom_emoji_id
return await _process_request(token, method_url, params=payload)
-async def edit_forum_topic(token, chat_id, message_thread_id, name, icon_custom_emoji_id):
+async def edit_forum_topic(token, chat_id, message_thread_id, name=None, icon_custom_emoji_id=None):
method_url = r'editForumTopic'
- payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id, 'name': name, 'icon_custom_emoji_id': icon_custom_emoji_id}
+ payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id}
+ if name is not None:
+ payload['name'] = name
+ if icon_custom_emoji_id is not None:
+ payload['icon_custom_emoji_id'] = icon_custom_emoji_id
return await _process_request(token, method_url, params=payload)
async def close_forum_topic(token, chat_id, message_thread_id):
@@ -1786,6 +1799,31 @@ async def get_forum_topic_icon_stickers(token):
method_url = r'getForumTopicIconStickers'
return await _process_request(token, method_url)
+async def edit_general_forum_topic(token, chat_id, name):
+ method_url = r'editGeneralForumTopic'
+ payload = {'chat_id': chat_id, 'name': name}
+ return await _process_request(token, method_url, params=payload)
+
+async def close_general_forum_topic(token, chat_id):
+ method_url = r'closeGeneralForumTopic'
+ payload = {'chat_id': chat_id}
+ return await _process_request(token, method_url, params=payload)
+
+async def reopen_general_forum_topic(token, chat_id):
+ method_url = r'reopenGeneralForumTopic'
+ payload = {'chat_id': chat_id}
+ return await _process_request(token, method_url, params=payload)
+
+async def hide_general_forum_topic(token, chat_id):
+ method_url = r'hideGeneralForumTopic'
+ payload = {'chat_id': chat_id}
+ return await _process_request(token, method_url, params=payload)
+
+async def unhide_general_forum_topic(token, chat_id):
+ method_url = r'unhideGeneralForumTopic'
+ payload = {'chat_id': chat_id}
+ return await _process_request(token, method_url, params=payload)
+
async def _convert_list_json_serializable(results):
ret = ''
for r in results:
diff --git a/telebot/types.py b/telebot/types.py
index 5e843d286..2938674bb 100644
--- a/telebot/types.py
+++ b/telebot/types.py
@@ -570,6 +570,14 @@ class Chat(JsonDeserializable):
automatically deleted; in seconds. Returned only in getChat.
:type message_auto_delete_time: :obj:`int`
+ :param has_aggressive_anti_spam_enabled: Optional. :obj:`bool`, if the chat has enabled aggressive anti-spam
+ protection. Returned only in getChat.
+ :type has_aggressive_anti_spam_enabled: :obj:`bool`
+
+ :param has_hidden_members: Optional. :obj:`bool`, if the chat has enabled hidden members. Returned only in
+ getChat.
+ :type has_hidden_members: :obj:`bool`
+
:param has_protected_content: Optional. :obj:`bool`, if messages from the chat can't be forwarded to other
chats. Returned only in getChat.
:type has_protected_content: :obj:`bool`
@@ -615,7 +623,8 @@ def __init__(self, id, type, title=None, username=None, first_name=None,
message_auto_delete_time=None, has_protected_content=None, sticker_set_name=None,
can_set_sticker_set=None, linked_chat_id=None, location=None,
join_to_send_messages=None, join_by_request=None, has_restricted_voice_and_video_messages=None,
- is_forum=None, active_usernames=None, emoji_status_custom_emoji_id=None, **kwargs):
+ is_forum=None, active_usernames=None, emoji_status_custom_emoji_id=None,
+ has_hidden_members=None, has_aggressive_anti_spam_enabled=None, **kwargs):
self.id: int = id
self.type: str = type
self.title: str = title
@@ -642,6 +651,8 @@ def __init__(self, id, type, title=None, username=None, first_name=None,
self.location: ChatLocation = location
self.active_usernames: List[str] = active_usernames
self.emoji_status_custom_emoji_id: str = emoji_status_custom_emoji_id
+ self.has_hidden_members: bool = has_hidden_members
+ self.has_aggressive_anti_spam_enabled: bool = has_aggressive_anti_spam_enabled
class MessageID(JsonDeserializable):
@@ -812,6 +823,9 @@ class Message(JsonDeserializable):
commands, etc. that appear in the caption
:type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity`
+ :param has_media_spoiler: Optional. True, if the message media is covered by a spoiler animation
+ :type has_media_spoiler: :obj:`bool`
+
:param contact: Optional. Message is a shared contact, information about the contact
:type contact: :class:`telebot.types.Contact`
@@ -892,6 +906,10 @@ class Message(JsonDeserializable):
Telegram Login »
:type connected_website: :obj:`str`
+ :param write_access_allowed: Optional. Service message: the user allowed the bot added to the attachment
+ menu to write messages
+ :type write_access_allowed: :class:`telebot.types.WriteAccessAllowed`
+
:param passport_data: Optional. Telegram Passport data
:type passport_data: :class:`telebot.types.PassportData`
@@ -902,12 +920,21 @@ class Message(JsonDeserializable):
:param forum_topic_created: Optional. Service message: forum topic created
:type forum_topic_created: :class:`telebot.types.ForumTopicCreated`
+ :param forum_topic_edited: Optional. Service message: forum topic edited
+ :type forum_topic_edited: :class:`telebot.types.ForumTopicEdited`
+
:param forum_topic_closed: Optional. Service message: forum topic closed
:type forum_topic_closed: :class:`telebot.types.ForumTopicClosed`
:param forum_topic_reopened: Optional. Service message: forum topic reopened
:type forum_topic_reopened: :class:`telebot.types.ForumTopicReopened`
+ :param general_forum_topic_hidden: Optional. Service message: the 'General' forum topic hidden
+ :type general_forum_topic_hidden: :class:`telebot.types.GeneralForumTopicHidden`
+
+ :param general_forum_topic_unhidden: Optional. Service message: the 'General' forum topic unhidden
+ :type general_forum_topic_unhidden: :class:`telebot.types.GeneralForumTopicUnhidden`
+
:param video_chat_scheduled: Optional. Service message: video chat scheduled
:type video_chat_scheduled: :class:`telebot.types.VideoChatScheduled`
@@ -1110,6 +1137,20 @@ def de_json(cls, json_string):
if 'forum_topic_reopened' in obj:
opts['forum_topic_reopened'] = ForumTopicReopened.de_json(obj['forum_topic_reopened'])
content_type = 'forum_topic_reopened'
+ if 'has_media_spoiler' in obj:
+ opts['has_media_spoiler'] = obj['has_media_spoiler']
+ if 'forum_topic_edited' in obj:
+ opts['forum_topic_edited'] = ForumTopicEdited.de_json(obj['forum_topic_edited'])
+ content_type = 'forum_topic_edited'
+ if 'general_forum_topic_hidden' in obj:
+ opts['general_forum_topic_hidden'] = GeneralForumTopicHidden.de_json(obj['general_forum_topic_hidden'])
+ content_type = 'general_forum_topic_hidden'
+ if 'general_forum_topic_unhidden' in obj:
+ opts['general_forum_topic_unhidden'] = GeneralForumTopicUnhidden.de_json(obj['general_forum_topic_unhidden'])
+ content_type = 'general_forum_topic_unhidden'
+ if 'write_access_allowed' in obj:
+ opts['write_access_allowed'] = WriteAccessAllowed.de_json(obj['write_access_allowed'])
+ content_type = 'write_access_allowed'
return cls(message_id, from_user, date, chat, content_type, opts, json_string)
@classmethod
@@ -1200,6 +1241,11 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso
self.forum_topic_created: Optional[ForumTopicCreated] = None
self.forum_topic_closed: Optional[ForumTopicClosed] = None
self.forum_topic_reopened: Optional[ForumTopicReopened] = None
+ self.has_media_spoiler: Optional[bool] = None
+ self.forum_topic_edited: Optional[ForumTopicEdited] = None
+ self.general_forum_topic_hidden: Optional[GeneralForumTopicHidden] = None
+ self.general_forum_topic_unhidden: Optional[GeneralForumTopicUnhidden] = None
+ self.write_access_allowed: Optional[WriteAccessAllowed] = None
for key in options:
setattr(self, key, options[key])
self.json = json_string
@@ -2055,13 +2101,21 @@ class ReplyKeyboardMarkup(JsonSerializable):
replies to the request with a keyboard to select the new language. Other users in the group don't see the keyboard.
:type selective: :obj:`bool`
+ :param is_persistent: Optional. Use this parameter if you want to show the keyboard to specific users only.
+ Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a
+ reply (has reply_to_message_id), sender of the original message.
+
+ Example: A user requests to change the bot's language, bot replies to the request with a keyboard to
+ select the new language. Other users in the group don't see the keyboard.
+
:return: Instance of the class
:rtype: :class:`telebot.types.ReplyKeyboardMarkup`
"""
max_row_keys = 12
def __init__(self, resize_keyboard: Optional[bool]=None, one_time_keyboard: Optional[bool]=None,
- selective: Optional[bool]=None, row_width: int=3, input_field_placeholder: Optional[str]=None):
+ selective: Optional[bool]=None, row_width: int=3, input_field_placeholder: Optional[str]=None,
+ is_persistent: Optional[bool]=None):
if row_width > self.max_row_keys:
# Todo: Will be replaced with Exception in future releases
if not DISABLE_KEYLEN_ERROR:
@@ -2074,6 +2128,7 @@ def __init__(self, resize_keyboard: Optional[bool]=None, one_time_keyboard: Opti
self.row_width: int = row_width
self.input_field_placeholder: str = input_field_placeholder
self.keyboard: List[List[KeyboardButton]] = []
+ self.is_persistent: bool = is_persistent
def add(self, *args, row_width=None):
"""
@@ -2139,6 +2194,8 @@ def to_json(self):
json_dict['selective'] = self.selective
if self.input_field_placeholder:
json_dict['input_field_placeholder'] = self.input_field_placeholder
+ if self.is_persistent is not None:
+ json_dict['is_persistent'] = self.is_persistent
return json.dumps(json_dict)
@@ -5781,18 +5838,26 @@ class InputMediaPhoto(InputMedia):
instead of parse_mode
:type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity`
+ :param has_spoiler: Optional. True, if the uploaded photo is a spoiler
+ :type has_spoiler: :obj:`bool`
+
:return: Instance of the class
:rtype: :class:`telebot.types.InputMediaPhoto`
"""
- def __init__(self, media, caption=None, parse_mode=None, caption_entities=None):
+ def __init__(self, media, caption=None, parse_mode=None, caption_entities=None, has_spoiler=None):
if util.is_pil_image(media):
media = util.pil_image_to_file(media)
super(InputMediaPhoto, self).__init__(
type="photo", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities)
+ self.has_spoiler: Optional[bool] = has_spoiler
+
def to_dict(self):
- return super(InputMediaPhoto, self).to_dict()
+ ret = super(InputMediaPhoto, self).to_dict()
+ if self.has_spoiler is not None:
+ ret['has_spoiler'] = self.has_spoiler
+ return ret
class InputMediaVideo(InputMedia):
@@ -5836,11 +5901,14 @@ class InputMediaVideo(InputMedia):
:param supports_streaming: Optional. Pass True, if the uploaded video is suitable for streaming
:type supports_streaming: :obj:`bool`
+ :param has_spoiler: Optional. True, if the uploaded video is a spoiler
+ :type has_spoiler: :obj:`bool`
+
:return: Instance of the class
:rtype: :class:`telebot.types.InputMediaVideo`
"""
def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=None,
- width=None, height=None, duration=None, supports_streaming=None):
+ width=None, height=None, duration=None, supports_streaming=None, has_spoiler=None):
super(InputMediaVideo, self).__init__(
type="video", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities)
self.thumb = thumb
@@ -5848,6 +5916,7 @@ def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_ent
self.height = height
self.duration = duration
self.supports_streaming = supports_streaming
+ self.has_spoiler: Optional[bool] = has_spoiler
def to_dict(self):
ret = super(InputMediaVideo, self).to_dict()
@@ -5861,6 +5930,8 @@ def to_dict(self):
ret['duration'] = self.duration
if self.supports_streaming:
ret['supports_streaming'] = self.supports_streaming
+ if self.has_spoiler is not None:
+ ret['has_spoiler'] = self.has_spoiler
return ret
@@ -5902,17 +5973,21 @@ class InputMediaAnimation(InputMedia):
:param duration: Optional. Animation duration in seconds
:type duration: :obj:`int`
+ :param has_spoiler: Optional. True, if the uploaded animation is a spoiler
+ :type has_spoiler: :obj:`bool`
+
:return: Instance of the class
:rtype: :class:`telebot.types.InputMediaAnimation`
"""
def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=None,
- width=None, height=None, duration=None):
+ width=None, height=None, duration=None, has_spoiler=None):
super(InputMediaAnimation, self).__init__(
type="animation", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities)
self.thumb = thumb
self.width = width
self.height = height
self.duration = duration
+ self.has_spoiler: Optional[bool] = has_spoiler
def to_dict(self):
ret = super(InputMediaAnimation, self).to_dict()
@@ -5924,6 +5999,8 @@ def to_dict(self):
ret['height'] = self.height
if self.duration:
ret['duration'] = self.duration
+ if self.has_spoiler is not None:
+ ret['has_spoiler'] = self.has_spoiler
return ret
@@ -6829,6 +6906,61 @@ def de_json(cls, json_string):
def __init__(self) -> None:
pass
+class ForumTopicEdited(JsonDeserializable):
+ """
+ This object represents a service message about an edited forum topic.
+
+ Telegram documentation: https://core.telegram.org/bots/api#forumtopicedited
+
+ :param name: Optional, Name of the topic(if updated)
+ :type name: :obj:`str`
+
+ :param icon_custom_emoji_id: Optional. New identifier of the custom emoji shown as the topic icon, if it was edited;
+ an empty string if the icon was removed
+ :type icon_custom_emoji_id: :obj:`str`
+ """
+ @classmethod
+ def de_json(cls, json_string):
+ if json_string is None: return None
+ obj = cls.check_json(json_string)
+ return cls(**obj)
+
+ def __init__(self, name: Optional[str]=None, icon_custom_emoji_id: Optional[str]=None) -> None:
+ self.name: Optional[str] = name
+ self.icon_custom_emoji_id: Optional[str] = icon_custom_emoji_id
+
+
+class GeneralForumTopicHidden(JsonDeserializable):
+ """
+ This object represents a service message about General forum topic hidden in the chat.
+ Currently holds no information.
+
+ Telegram documentation: https://core.telegram.org/bots/api#generalforumtopichidden
+ """
+ @classmethod
+ def de_json(cls, json_string):
+ return cls()
+
+ def __init__(self) -> None:
+ pass
+
+
+class GeneralForumTopicUnhidden(JsonDeserializable):
+ """
+ This object represents a service message about General forum topic unhidden in the chat.
+ Currently holds no information.
+
+ Telegram documentation: https://core.telegram.org/bots/api#generalforumtopicunhidden
+ """
+
+ @classmethod
+ def de_json(cls, json_string):
+ return cls()
+
+ def __init__(self) -> None:
+ pass
+
+
class ForumTopic(JsonDeserializable):
"""
@@ -6865,6 +6997,19 @@ def __init__(self, message_thread_id: int, name: str, icon_color: int, icon_cust
self.icon_custom_emoji_id: Optional[str] = icon_custom_emoji_id
+class WriteAccessAllowed(JsonDeserializable):
+ """
+ This object represents a service message about a user allowed to post messages in the chat.
+ Currently holds no information.
+
+ Telegram documentation: https://core.telegram.org/bots/api#writeaccessallowed
+ """
+ @classmethod
+ def de_json(cls, json_string):
+ return cls()
+
+ def __init__(self) -> None:
+ pass