From cd4dc899a197c475bdc481069547b63080d49186 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 2 Dec 2022 23:46:26 +0300 Subject: [PATCH 01/28] Fix caption_entities miss in InputMediaXXX --- telebot/types.py | 47 ++++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index f6a03c4..5e843d2 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -5765,9 +5765,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://” to upload a new one using multipart/form-data under name. More information on Sending Files » @@ -5787,11 +5784,12 @@ class InputMediaPhoto(InputMedia): :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): 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) def to_dict(self): return super(InputMediaPhoto, self).to_dict() @@ -5803,9 +5801,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://” to upload a new one using multipart/form-data under name. More information on Sending Files » @@ -5844,9 +5839,10 @@ class InputMediaVideo(InputMedia): :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): + 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 @@ -5874,9 +5870,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://” to upload a new one using multipart/form-data under name. More information on Sending Files » @@ -5912,8 +5905,10 @@ class InputMediaAnimation(InputMedia): :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): + 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 @@ -5938,9 +5933,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://” to upload a new one using multipart/form-data under name. More information on Sending Files » @@ -5976,8 +5968,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 +5996,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://” to upload a new one using multipart/form-data under name. More information on Sending Files » :type media: :obj:`str` @@ -6035,8 +6026,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 From 43abedbff7cc51961477db37099763cb545b5f0d Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 2 Dec 2022 23:59:59 +0300 Subject: [PATCH 02/28] Fixed register_next_step_handler_by_chat_id chat_id description --- telebot/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6c0a73d..eebfe30 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4981,14 +4981,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]` From 34acae9a59e182059c973ee29e1faa57963ed1d2 Mon Sep 17 00:00:00 2001 From: reddere <111050360+reddere@users.noreply.github.com> Date: Sat, 3 Dec 2022 13:33:22 +0100 Subject: [PATCH 03/28] fixing escape() fixing escape() as replacing a None would throw an exception 'NoneType' object has no attribute 'replace'. useful in case of escaping a None string given from message.from_user.last_name as you dont know wether the user has a last name or not --- telebot/util.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 485d8e6..5eee539 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -399,8 +399,12 @@ def escape(text: str) -> str: :return: the escaped text """ chars = {"&": "&", "<": "<", ">": ">"} - for old, new in chars.items(): text = text.replace(old, new) - return text + if text == None: + return None + else: + for old, new in chars.items(): + text = text.replace(old, new) + return text def user_link(user: types.User, include_id: bool=False) -> str: From 669c18fdc01fbcbc19315f87275301045631064d Mon Sep 17 00:00:00 2001 From: reddere <111050360+reddere@users.noreply.github.com> Date: Sat, 3 Dec 2022 14:11:07 +0100 Subject: [PATCH 04/28] update updated "== None" to "is None" and adjusted the else statement --- telebot/util.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 5eee539..86528a8 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -399,11 +399,10 @@ def escape(text: str) -> str: :return: the escaped text """ chars = {"&": "&", "<": "<", ">": ">"} - if text == None: + if text is None: return None else: - for old, new in chars.items(): - text = text.replace(old, new) + for old, new in chars.items(): text = text.replace(old, new) return text From ae20cb9f315c0af45973ebf064c4d22f33c1b67f Mon Sep 17 00:00:00 2001 From: reddere <111050360+reddere@users.noreply.github.com> Date: Sat, 3 Dec 2022 14:21:31 +0100 Subject: [PATCH 05/28] Update util.py --- telebot/util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 86528a8..cc0dae3 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -401,8 +401,7 @@ def escape(text: str) -> str: chars = {"&": "&", "<": "<", ">": ">"} if text is None: return None - else: - for old, new in chars.items(): text = text.replace(old, new) + for old, new in chars.items(): text = text.replace(old, new) return text From 4ed460b137685846f75de219f84440fb21200fe6 Mon Sep 17 00:00:00 2001 From: reddere <111050360+reddere@users.noreply.github.com> Date: Sat, 3 Dec 2022 14:23:10 +0100 Subject: [PATCH 06/28] Update util.py --- telebot/util.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index cc0dae3..e52ee83 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -401,8 +401,9 @@ def escape(text: str) -> str: chars = {"&": "&", "<": "<", ">": ">"} if text is None: return None - for old, new in chars.items(): text = text.replace(old, new) - return text + for old, new in chars.items(): + text = text.replace(old, new) + return text def user_link(user: types.User, include_id: bool=False) -> str: From 45fe2ea31976e8308cdde8aa180950e0038631eb Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 13 Dec 2022 18:44:41 +0400 Subject: [PATCH 07/28] Update reloader.py --- telebot/ext/reloader.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/telebot/ext/reloader.py b/telebot/ext/reloader.py index d57453f..b6fd49b 100644 --- a/telebot/ext/reloader.py +++ b/telebot/ext/reloader.py @@ -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) From add240adfdcb5584b978349e8953aa44e34b38a2 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 14 Dec 2022 12:41:30 +0400 Subject: [PATCH 08/28] Update asyncio_helper.py --- telebot/asyncio_helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 6118d84..9f5b59a 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -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): From 171172d12eef05da18793de0bacc85e98331692d Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 19 Dec 2022 17:12:15 +0400 Subject: [PATCH 09/28] Update async_telebot.py --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 1083a28..983fc25 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -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(self, secret_token, listen, port, ssl_context, '/'+url_path) await self.webhook_listener.run_app() From 5b279b7ad9f821e0f7d8fd53eaf06428b1ef8d4a Mon Sep 17 00:00:00 2001 From: coder2020official Date: Mon, 19 Dec 2022 17:28:05 +0400 Subject: [PATCH 10/28] (A)SyncWebhookListener changes --- telebot/__init__.py | 2 +- telebot/async_telebot.py | 2 +- telebot/ext/aio/webhooks.py | 3 ++- telebot/ext/sync/webhooks.py | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6c0a73d..fef57c6 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -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() diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 983fc25..136d453 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -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) + 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() diff --git a/telebot/ext/aio/webhooks.py b/telebot/ext/aio/webhooks.py index 56af592..5a692b3 100644 --- a/telebot/ext/aio/webhooks.py +++ b/telebot/ext/aio/webhooks.py @@ -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, diff --git a/telebot/ext/sync/webhooks.py b/telebot/ext/sync/webhooks.py index e15e8c8..0d41b72 100644 --- a/telebot/ext/sync/webhooks.py +++ b/telebot/ext/sync/webhooks.py @@ -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, From 925f7012f1230b2d49f64fb10b2a5bf7bc4712e7 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 20 Dec 2022 23:44:32 +0400 Subject: [PATCH 11/28] Update formatting.py --- telebot/formatting.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/formatting.py b/telebot/formatting.py index 7b6a9f3..4af8263 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -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'' \ No newline at end of file + return f'' From c5e733a4c14d9e17c5e37acc46129b10556025e4 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 20 Dec 2022 23:47:12 +0400 Subject: [PATCH 12/28] Escape both metachars --- telebot/formatting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/formatting.py b/telebot/formatting.py index 4af8263..ec421c6 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -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 From 2aaab0851784c68e279cb7d1b41c2f855c3a3778 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Dec 2022 19:49:24 +0000 Subject: [PATCH 13/28] Bump wheel from 0.24.0 to 0.38.1 Bumps [wheel](https://github.com/pypa/wheel) from 0.24.0 to 0.38.1. - [Release notes](https://github.com/pypa/wheel/releases) - [Changelog](https://github.com/pypa/wheel/blob/main/docs/news.rst) - [Commits](https://github.com/pypa/wheel/compare/0.24.0...0.38.1) --- updated-dependencies: - dependency-name: wheel dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d2b8256..5168828 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ pytest requests==2.20.0 -wheel==0.24.0 +wheel==0.38.1 aiohttp>=3.8.0,<3.9.0 \ No newline at end of file From 2f25b5665968747c66fd73102ef8c3576dfb1057 Mon Sep 17 00:00:00 2001 From: Rudy Ayitinya Sulley Date: Tue, 27 Dec 2022 02:46:07 +0000 Subject: [PATCH 14/28] Update README.md Adds a bot to Bots using this library list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e4e70e6..b8c4a6c 100644 --- a/README.md +++ b/README.md @@ -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. From 24cd014410280748252d3e50c034e81a1344306f Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 30 Dec 2022 18:52:00 +0400 Subject: [PATCH 15/28] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b8c4a6c..5eb412c 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!

Official documentation

From f0a1cefdda5b846ff3bb28c8042215d26b7d09e0 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 19:08:37 +0400 Subject: [PATCH 16/28] Added the field is_persistent to the class ReplyKeyboardMarkup Added the field is_persistent to the class ReplyKeyboardMarkup, allowing to control when the keyboard is shown. --- telebot/types.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 5e843d2..b510962 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2055,13 +2055,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 +2082,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 +2148,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) From 4d11e97c25b8920492e8e2ac7c525629e8085568 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 19:20:23 +0400 Subject: [PATCH 17/28] has_spoiler parameter Added the parameter has_spoiler to the methods sendPhoto, sendVideo, and sendAnimation. --- telebot/__init__.py | 24 ++++++++++++++++++------ telebot/apihelper.py | 13 ++++++++++--- telebot/async_telebot.py | 24 ++++++++++++++++++------ telebot/asyncio_helper.py | 13 ++++++++++--- 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6a4dd7b..bf94fd5 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -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( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index b25e036..005ac33 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') @@ -666,7 +668,7 @@ def send_chat_action(token, chat_id, action, timeout=None): 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 +712,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 +763,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') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index e8249fc..bbaeb53 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -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], diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 9f5b59a..2ae1934 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') @@ -658,7 +660,7 @@ async def send_chat_action(token, chat_id, action, timeout=None): 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 +704,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 +755,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') From 4537b237c80ce7f257c721dffdd4e554e62dc32e Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 19:27:38 +0400 Subject: [PATCH 18/28] has_spoiler for types.py Added the field has_spoiler to the classes InputMediaPhoto, InputMediaVideo, and InputMediaAnimation. --- telebot/types.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index b510962..bf1c8b1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -5792,18 +5792,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: + ret['has_spoiler'] = self.has_spoiler + return ret class InputMediaVideo(InputMedia): @@ -5847,11 +5855,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 @@ -5859,6 +5870,7 @@ class InputMediaVideo(InputMedia): 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() @@ -5872,6 +5884,8 @@ class InputMediaVideo(InputMedia): ret['duration'] = self.duration if self.supports_streaming: ret['supports_streaming'] = self.supports_streaming + if self.has_spoiler: + ret['has_spoiler'] = self.has_spoiler return ret @@ -5913,17 +5927,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() @@ -5935,6 +5953,8 @@ class InputMediaAnimation(InputMedia): ret['height'] = self.height if self.duration: ret['duration'] = self.duration + if self.has_spoiler: + ret['has_spoiler'] = self.has_spoiler return ret From 9f5d9861a489fa2d1e924e0556ca4a74010aac3d Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 19:41:46 +0400 Subject: [PATCH 19/28] Added the field has_media_spoiler to the class Message. Added the field has_media_spoiler to the class Message. --- telebot/types.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index bf1c8b1..266f80c 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -812,6 +812,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` @@ -1110,6 +1113,8 @@ 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'] return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod @@ -1200,6 +1205,7 @@ 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 for key in options: setattr(self, key, options[key]) self.json = json_string From 107f92314bcd161c7d31a7bad621af13b43d4343 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 19:50:14 +0400 Subject: [PATCH 20/28] icon_custom_emoji_id and name parameters made optional for edit_forum_topic The parameters name and icon_custom_emoji_id of the method editForumTopic are now optional. If they are omitted, the existing values are kept. --- telebot/__init__.py | 11 +++++++---- telebot/apihelper.py | 8 ++++++-- telebot/async_telebot.py | 11 +++++++---- telebot/asyncio_helper.py | 8 ++++++-- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index bf94fd5..95ec936 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4648,8 +4648,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 @@ -4664,10 +4664,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. diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 005ac33..4f56705 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1773,9 +1773,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): diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index bbaeb53..65accb8 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5514,8 +5514,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 @@ -5530,10 +5530,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. diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 2ae1934..8bbdc3d 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1764,9 +1764,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): From a20a3ae32179489fc7d2d345316c891a830c1cdd Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 20:07:38 +0400 Subject: [PATCH 21/28] topic events and write_access_allowed Added the classes ForumTopicEdited, GeneralForumTopicHidden, GeneralForumTopicUnhidden, and WriteAccessAllowed and the fields forum_topic_edited, general_forum_topic_hidden, general_forum_topic_unhidden, and write_access_allowed to the class Message. --- telebot/types.py | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 266f80c..0159477 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -895,6 +895,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` @@ -905,12 +909,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` @@ -1115,6 +1128,18 @@ class Message(JsonDeserializable): 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 @@ -1206,6 +1231,10 @@ class Message(JsonDeserializable): 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 @@ -6866,6 +6895,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): """ @@ -6902,6 +6986,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 From f297ad23c7511f50223a46c9fe00384a13ad2abd Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 20:19:50 +0400 Subject: [PATCH 22/28] Added methods for topic management Added the methods editGeneralForumTopic, closeGeneralForumTopic, reopenGeneralForumTopic, hideGeneralForumTopic, unhideGeneralForumTopic for managing the General topic in forums. --- telebot/__init__.py | 69 +++++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 25 ++++++++++++++ telebot/async_telebot.py | 69 +++++++++++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 25 ++++++++++++++ 4 files changed, 188 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 95ec936..fa0c11b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4754,6 +4754,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. diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 4f56705..f1fdb7c 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1813,6 +1813,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 65accb8..0ee8f06 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5620,6 +5620,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. diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 8bbdc3d..86468d0 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1797,6 +1797,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: From 9f8256607afa58472b78eb1cb46fdd0a87abf330 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 20:23:53 +0400 Subject: [PATCH 23/28] Added the parameter message_thread_id to the method sendChatAction for sending chat actions to a specific message thread or a forum topic. Added the parameter message_thread_id to the method sendChatAction for sending chat actions to a specific message thread or a forum topic. --- telebot/__init__.py | 7 +++++-- telebot/apihelper.py | 4 +++- telebot/async_telebot.py | 7 +++++-- telebot/asyncio_helper.py | 4 +++- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index fa0c11b..ef8ab71 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2806,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). @@ -2829,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( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index f1fdb7c..1086f54 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -657,11 +657,13 @@ 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) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 0ee8f06..6e3ec9a 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3664,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). @@ -3687,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, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 86468d0..4371885 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -649,11 +649,13 @@ 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) From eed56be59645b9d3c8c9c24982ace3e81c63ef56 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 20:38:26 +0400 Subject: [PATCH 24/28] Added fields has_hidden_members and has_aggressive_anti_spam_enabled to class Chat --- telebot/types.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 0159477..efcf6de 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 @@ 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): From 667e82d0736c3b6e7a45b01d66008b4fd96016c3 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 2 Jan 2023 17:54:35 +0400 Subject: [PATCH 25/28] Update telebot/types.py --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index efcf6de..920fecc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -5855,7 +5855,7 @@ class InputMediaPhoto(InputMedia): def to_dict(self): ret = super(InputMediaPhoto, self).to_dict() - if self.has_spoiler: + if self.has_spoiler is not None: ret['has_spoiler'] = self.has_spoiler return ret From 267a33c3298c76105f2a6d17573d0ee18770b4f4 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 2 Jan 2023 17:55:14 +0400 Subject: [PATCH 26/28] Update telebot/types.py --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 920fecc..8a5cfa4 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -5930,7 +5930,7 @@ class InputMediaVideo(InputMedia): ret['duration'] = self.duration if self.supports_streaming: ret['supports_streaming'] = self.supports_streaming - if self.has_spoiler: + if self.has_spoiler is not None: ret['has_spoiler'] = self.has_spoiler return ret From 3be5015f9e79d9b691c9d164cf74633dd9507f59 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 2 Jan 2023 17:55:30 +0400 Subject: [PATCH 27/28] Update telebot/types.py --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 8a5cfa4..2938674 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -5999,7 +5999,7 @@ class InputMediaAnimation(InputMedia): ret['height'] = self.height if self.duration: ret['duration'] = self.duration - if self.has_spoiler: + if self.has_spoiler is not None: ret['has_spoiler'] = self.has_spoiler return ret From 43cc2036545964a47e8a1779e02f113d3a9a55c9 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 2 Jan 2023 18:00:20 +0300 Subject: [PATCH 28/28] Bump version to 4.9/0 --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 360e2a4..e410739 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -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 --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index c757e00..1e28db8 100644 --- a/telebot/version.py +++ b/telebot/version.py @@ -1,3 +1,3 @@ # Versions should comply with PEP440. # This line is parsed in setup.py: -__version__ = '4.8.0' +__version__ = '4.9.0'