Merge pull request #1 from eternnoir/master

Update fork
This commit is contained in:
Konstantin Ostashenko 2023-01-02 17:12:49 +02:00 committed by GitHub
commit 6d4d3f8005
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 485 additions and 85 deletions

View File

@ -11,7 +11,7 @@
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.</p>
<p align="center">Both synchronous and asynchronous.</p>
## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#november-5-2022">6.3</a>!
## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#december-30-2022">6.4</a>!
<h2><a href='https://pytba.readthedocs.io/en/latest/index.html'>Official documentation</a></h2>
@ -877,6 +877,7 @@ Here are some examples of template:
* [GrandQuiz Bot](https://github.com/Carlosma7/TFM-GrandQuiz) by [Carlosma7](https://github.com/Carlosma7). This bot is a trivia game that allows you to play with people from different ages. This project addresses the use of a system through chatbots to carry out a social and intergenerational game as an alternative to traditional game development.
* [Diccionario de la RAE](https://t.me/dleraebot) ([source](https://github.com/studentenherz/dleraebot)) This bot lets you find difinitions of words in Spanish using [RAE's dictionary](https://dle.rae.es/). It features direct message and inline search.
* [remoteTelegramShell](https://github.com/EnriqueMoran/remoteTelegramShell) by [EnriqueMoran](https://github.com/EnriqueMoran). Control your LinuxOS computer through Telegram.
* [Commerce Telegram Bot](https://github.com/ayitinya/commerce-telegram-bot/). Make purchases of items in a store with an Admin panel for data control and notifications.
* [Pyfram-telegram-bot](https://github.com/skelly37/pyfram-telegram-bot) Query wolframalpha.com and make use of its API through Telegram.
* [TranslateThisVideoBot](https://gitlab.com/WuerfelDev/translatethisvideo) This Bot can understand spoken text in videos and translate it to English
* [Zyprexa](https://t.me/mathemathicsBot) ([source](https://github.com/atif5/zyprexa)) Zyprexa can solve, help you solve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX.

View File

@ -22,7 +22,7 @@ copyright = '2022, coder2020official'
author = 'coder2020official'
# The full version, including alpha/beta/rc tags
release = '4.8.0'
release = '4.9.0'
# -- General configuration ---------------------------------------------------

View File

@ -1,4 +1,4 @@
pytest
requests==2.20.0
wheel==0.24.0
wheel==0.38.1
aiohttp>=3.8.0,<3.9.0

View File

@ -540,7 +540,7 @@ class TeleBot:
from telebot.ext.sync import SyncWebhookListener
except (NameError, ImportError):
raise ImportError("Please install uvicorn and fastapi in order to use `run_webhooks` method.")
self.webhook_listener = SyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path)
self.webhook_listener = SyncWebhookListener(bot=self, secret_token=secret_token, host=listen, port=port, ssl_context=ssl_context, url_path='/'+url_path)
self.webhook_listener.run_app()
@ -1763,7 +1763,8 @@ class TeleBot:
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 @@ class TeleBot:
: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 @@ class TeleBot:
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 @@ class TeleBot:
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 @@ class TeleBot:
: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 @@ class TeleBot:
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 @@ class TeleBot:
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 @@ class TeleBot:
: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 @@ class TeleBot:
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 @@ class TeleBot:
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 @@ class TeleBot:
: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 @@ class TeleBot:
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 @@ class TeleBot:
: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 @@ class TeleBot:
"""
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.
@ -4981,14 +5068,14 @@ class TeleBot:
self.current_states.set_data(chat_id, user_id, key, value)
def register_next_step_handler_by_chat_id(
self, chat_id: Union[int, str], callback: Callable, *args, **kwargs) -> None:
self, chat_id: int, callback: Callable, *args, **kwargs) -> None:
"""
Registers a callback function to be notified when new message arrives after `message`.
Registers a callback function to be notified when new message arrives in the given chat.
Warning: In case `callback` as lambda function, saving next step handlers will not work.
:param chat_id: The chat for which we want to handle new message.
:type chat_id: :obj:`int` or :obj:`str`
:param chat_id: The chat (chat ID) for which we want to handle new message.
:type chat_id: :obj:`int`
:param callback: The callback function which next new message arrives.
:type callback: :obj:`Callable[[telebot.types.Message], None]`

View File

@ -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 = ''

View File

@ -2116,7 +2116,7 @@ class AsyncTeleBot:
from telebot.ext.aio import AsyncWebhookListener
except (NameError, ImportError):
raise ImportError("Please install uvicorn and fastapi in order to use `run_webhooks` method.")
self.webhook_listener = AsyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path, debug)
self.webhook_listener = AsyncWebhookListener(bot=self, secret_token=secret_token, host=listen, port=port, ssl_context=ssl_context, url_path='/'+url_path)
await self.webhook_listener.run_app()
@ -2627,7 +2627,8 @@ class AsyncTeleBot:
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 @@ class AsyncTeleBot:
: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 @@ class AsyncTeleBot:
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 @@ class AsyncTeleBot:
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 @@ class AsyncTeleBot:
: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 @@ class AsyncTeleBot:
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 @@ class AsyncTeleBot:
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 @@ class AsyncTeleBot:
: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 @@ class AsyncTeleBot:
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 @@ class AsyncTeleBot:
)
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 @@ class AsyncTeleBot:
: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 @@ class AsyncTeleBot:
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 @@ class AsyncTeleBot:
: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 @@ class AsyncTeleBot:
"""
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.

View File

@ -171,10 +171,10 @@ async def get_file(token, file_id):
async def get_file_url(token, file_id):
if FILE_URL is None:
return "https://api.telegram.org/file/bot{0}/{1}".format(token, await get_file(token, file_id)['file_path'])
return "https://api.telegram.org/file/bot{0}/{1}".format(token, (await get_file(token, file_id))['file_path'])
else:
# noinspection PyUnresolvedReferences
return FILE_URL.format(token, await get_file(token, file_id)['file_path'])
return FILE_URL.format(token, (await get_file(token, file_id))['file_path'])
async def download_file(token, file_path):
@ -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:

View File

@ -25,7 +25,8 @@ from typing import Optional
class AsyncWebhookListener:
def __init__(self, bot,
secret_token: str, host: Optional[str]="127.0.0.1",
secret_token: str,
host: Optional[str]="127.0.0.1",
port: Optional[int]=443,
ssl_context: Optional[tuple]=None,
url_path: Optional[str]=None,

View File

@ -18,8 +18,14 @@ def restart_file():
p = psutil.Process(os.getpid())
for handler in p.open_files() + p.connections():
os.close(handler.fd)
except OSError:
pass
except Exception as e:
logger.error(e)
python = sys.executable
os.execl(python, python, *sys.argv)
if os.name == 'nt':
os.execv(sys.executable, ['python'] + sys.argv)
else:
os.execl(python, python, *sys.argv)

View File

@ -21,7 +21,8 @@ from typing import Optional
class SyncWebhookListener:
def __init__(self, bot,
secret_token: str, host: Optional[str]="127.0.0.1",
secret_token: str,
host: Optional[str]="127.0.0.1",
port: Optional[int]=443,
ssl_context: Optional[tuple]=None,
url_path: Optional[str]=None,

View File

@ -61,8 +61,8 @@ def escape_markdown(content: str) -> str:
:rtype: :obj:`str`
"""
parse = re.sub(r"([_*\[\]()~`>\#\+\-=|\.!])", r"\\\1", content)
reparse = re.sub(r"\\\\([_*\[\]()~`>\#\+\-=|\.!])", r"\1", parse)
parse = re.sub(r"([_*\[\]()~`>\#\+\-=|\.!\{\}])", r"\\\1", content)
reparse = re.sub(r"\\\\([_*\[\]()~`>\#\+\-=|\.!\{\}])", r"\1", parse)
return reparse
@ -323,4 +323,4 @@ def hide_link(url: str) -> str:
:return: The hidden url.
:rtype: :obj:`str`
"""
return f'<a href="{url}">&#8288;</a>'
return f'<a href="{url}">&#8288;</a>'

View File

@ -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 @@ class Chat(JsonDeserializable):
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 @@ class Chat(JsonDeserializable):
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 @@ class Message(JsonDeserializable):
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 @@ class Message(JsonDeserializable):
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 @@ class ReplyKeyboardMarkup(JsonSerializable):
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 @@ class ReplyKeyboardMarkup(JsonSerializable):
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)
@ -5765,9 +5822,6 @@ class InputMediaPhoto(InputMedia):
Telegram Documentation: https://core.telegram.org/bots/api#inputmediaphoto
:param type: Type of the result, must be photo
:type type: :obj:`str`
:param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an
HTTP URL for Telegram to get a file from the Internet, or pass attach://<file_attach_name> to upload a new one using
multipart/form-data under <file_attach_name> name. More information on Sending Files »
@ -5784,17 +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):
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)
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):
@ -5803,9 +5866,6 @@ class InputMediaVideo(InputMedia):
Telegram Documentation: https://core.telegram.org/bots/api#inputmediavideo
:param type: Type of the result, must be video
:type type: :obj:`str`
:param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an
HTTP URL for Telegram to get a file from the Internet, or pass attach://<file_attach_name> to upload a new one using
multipart/form-data under <file_attach_name> name. More information on Sending Files »
@ -5841,17 +5901,22 @@ 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, width=None, height=None, duration=None,
supports_streaming=None):
super(InputMediaVideo, self).__init__(type="video", media=media, caption=caption, parse_mode=parse_mode)
def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=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
self.width = width
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()
@ -5865,6 +5930,8 @@ class InputMediaVideo(InputMedia):
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
@ -5874,9 +5941,6 @@ class InputMediaAnimation(InputMedia):
Telegram Documentation: https://core.telegram.org/bots/api#inputmediaanimation
:param type: Type of the result, must be animation
:type type: :obj:`str`
:param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an
HTTP URL for Telegram to get a file from the Internet, or pass attach://<file_attach_name> to upload a new one using
multipart/form-data under <file_attach_name> name. More information on Sending Files »
@ -5909,15 +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, width=None, height=None, duration=None):
super(InputMediaAnimation, self).__init__(type="animation", media=media, caption=caption, parse_mode=parse_mode)
def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=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()
@ -5929,6 +5999,8 @@ class InputMediaAnimation(InputMedia):
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
@ -5938,9 +6010,6 @@ class InputMediaAudio(InputMedia):
Telegram Documentation: https://core.telegram.org/bots/api#inputmediaaudio
:param type: Type of the result, must be audio
:type type: :obj:`str`
:param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an
HTTP URL for Telegram to get a file from the Internet, or pass attach://<file_attach_name> to upload a new one using
multipart/form-data under <file_attach_name> name. More information on Sending Files »
@ -5976,8 +6045,10 @@ class InputMediaAudio(InputMedia):
:return: Instance of the class
:rtype: :class:`telebot.types.InputMediaAudio`
"""
def __init__(self, media, thumb=None, caption=None, parse_mode=None, duration=None, performer=None, title=None):
super(InputMediaAudio, self).__init__(type="audio", media=media, caption=caption, parse_mode=parse_mode)
def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=None,
duration=None, performer=None, title=None):
super(InputMediaAudio, self).__init__(
type="audio", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities)
self.thumb = thumb
self.duration = duration
self.performer = performer
@ -6002,10 +6073,7 @@ class InputMediaDocument(InputMedia):
Telegram Documentation: https://core.telegram.org/bots/api#inputmediadocument
:param type: Type of the result, must be document
:type type: :obj:`str`
:param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an
:param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an
HTTP URL for Telegram to get a file from the Internet, or pass attach://<file_attach_name> to upload a new one using
multipart/form-data under <file_attach_name> name. More information on Sending Files »
:type media: :obj:`str`
@ -6035,8 +6103,10 @@ class InputMediaDocument(InputMedia):
:return: Instance of the class
:rtype: :class:`telebot.types.InputMediaDocument`
"""
def __init__(self, media, thumb=None, caption=None, parse_mode=None, disable_content_type_detection=None):
super(InputMediaDocument, self).__init__(type="document", media=media, caption=caption, parse_mode=parse_mode)
def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=None,
disable_content_type_detection=None):
super(InputMediaDocument, self).__init__(
type="document", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities)
self.thumb = thumb
self.disable_content_type_detection = disable_content_type_detection
@ -6836,6 +6906,61 @@ class ForumTopicReopened(JsonDeserializable):
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):
"""
@ -6872,6 +6997,19 @@ class ForumTopic(JsonDeserializable):
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

View File

@ -399,7 +399,10 @@ def escape(text: str) -> str:
:return: the escaped text
"""
chars = {"&": "&amp;", "<": "&lt;", ">": "&gt;"}
for old, new in chars.items(): text = text.replace(old, new)
if text is None:
return None
for old, new in chars.items():
text = text.replace(old, new)
return text

View File

@ -1,3 +1,3 @@
# Versions should comply with PEP440.
# This line is parsed in setup.py:
__version__ = '4.8.0'
__version__ = '4.9.0'