From be440a155ac416f75632a962cbfd65773ef659e9 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 May 2024 11:39:44 +0300 Subject: [PATCH 01/16] Init BOT API 7.3 with version bump --- docs/source/conf.py | 2 +- pyproject.toml | 2 +- telebot/version.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index aa49bceb2..bb40d90ab 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ copyright = f'2022-{datetime.now().year}, {author}' # The full version, including alpha/beta/rc tags -release = '4.17.0' +release = '4.18.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index fde7a8685..6666fee8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.17.0" +version = "4.18.0" description = "Python Telegram bot api." authors = [{name = "eternnoir", email = "eternnoir@gmail.com"}] license = {text = "GPL2"} diff --git a/telebot/version.py b/telebot/version.py index 3eb8b787f..4ee703d3d 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.17.0' +__version__ = '4.18.0' From 8a185b9f0cd362807a33c04295a6a4bb42404de3 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 May 2024 11:46:14 +0300 Subject: [PATCH 02/16] Added the field via_join_request to the class ChatMemberUpdated. --- telebot/types.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 51db25ab5..34e7ab328 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -284,6 +284,9 @@ class ChatMemberUpdated(JsonDeserializable): link events only. :type invite_link: :class:`telebot.types.ChatInviteLink` + :param via_join_request: Optional. True, if the user joined the chat after sending a direct join request without using an invite link and being approved by an administrator + :type via_join_request: :obj:`bool` + :param via_chat_folder_invite_link: Optional. True, if the user joined the chat via a chat folder invite link :type via_chat_folder_invite_link: :obj:`bool` @@ -301,7 +304,8 @@ def de_json(cls, json_string): obj['invite_link'] = ChatInviteLink.de_json(obj.get('invite_link')) return cls(**obj) - def __init__(self, chat, from_user, date, old_chat_member, new_chat_member, invite_link=None, via_chat_folder_invite_link=None, + def __init__(self, chat, from_user, date, old_chat_member, new_chat_member, invite_link=None, + via_join_request=None, via_chat_folder_invite_link=None, **kwargs): self.chat: Chat = chat self.from_user: User = from_user @@ -309,8 +313,9 @@ def __init__(self, chat, from_user, date, old_chat_member, new_chat_member, invi self.old_chat_member: ChatMember = old_chat_member self.new_chat_member: ChatMember = new_chat_member self.invite_link: Optional[ChatInviteLink] = invite_link + self.via_join_request: Optional[bool] = via_join_request self.via_chat_folder_invite_link: Optional[bool] = via_chat_folder_invite_link - + @property def difference(self) -> Dict[str, List]: """ From 0e462918d844c262baf5e409314aeaf18c7068d0 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 May 2024 11:50:31 +0300 Subject: [PATCH 03/16] Documented that .MP3 and .M4A files can be used as voice messages. --- telebot/__init__.py | 4 +--- telebot/async_telebot.py | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3da8952be..d953df750 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2356,9 +2356,7 @@ def send_voice( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None) -> types.Message: """ - Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. - For this to work, your audio must be in an .OGG file encoded with OPUS (other formats may be sent as Audio or Document). - On success, the sent Message is returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future. + Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. For this to work, your audio must be in an .OGG file encoded with OPUS, or in .MP3 format, or in .M4A format (other formats may be sent as Audio or Document). On success, the sent Message is returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future. Telegram documentation: https://core.telegram.org/bots/api#sendvoice diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index fa4a0a7eb..473a06436 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3764,9 +3764,7 @@ async def send_voice( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None) -> types.Message: """ - Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. - For this to work, your audio must be in an .OGG file encoded with OPUS (other formats may be sent as Audio or Document). - On success, the sent Message is returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future. + Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. For this to work, your audio must be in an .OGG file encoded with OPUS, or in .MP3 format, or in .M4A format (other formats may be sent as Audio or Document). On success, the sent Message is returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future. Telegram documentation: https://core.telegram.org/bots/api#sendvoice From bf8c6f765c8f13759c05449bc41fcca091b62c2d Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 May 2024 11:55:17 +0300 Subject: [PATCH 04/16] Added support for live locations that can be edited indefinitely, allowing 0x7FFFFFFF to be used as live_period. --- telebot/__init__.py | 2 +- telebot/async_telebot.py | 2 +- telebot/types.py | 18 ++++++------------ 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d953df750..15bfd5dc0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3160,7 +3160,7 @@ def send_location( :param longitude: Longitude of the location :type longitude: :obj:`float` - :param live_period: Period in seconds for which the location will be updated (see Live Locations, should be between 60 and 86400. + :param live_period: Period in seconds during which the location will be updated (see Live Locations, should be between 60 and 86400, or 0x7FFFFFFF for live locations that can be edited indefinitely. :type live_period: :obj:`int` :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 473a06436..435f74428 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4571,7 +4571,7 @@ async def send_location( :param longitude: Longitude of the location :type longitude: :obj:`float` - :param live_period: Period in seconds for which the location will be updated (see Live Locations, should be between 60 and 86400. + :param live_period: Period in seconds during which the location will be updated (see Live Locations, should be between 60 and 86400, or 0x7FFFFFFF for live locations that can be edited indefinitely. :type live_period: :obj:`int` :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message diff --git a/telebot/types.py b/telebot/types.py index 34e7ab328..0ae4c53c0 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -3897,16 +3897,13 @@ class InputLocationMessageContent(Dictionaryable): :param horizontal_accuracy: Optional. The radius of uncertainty for the location, measured in meters; 0-1500 :type horizontal_accuracy: :obj:`float` number - :param live_period: Optional. Period in seconds for which the location can be updated, should be between 60 and - 86400. + :param live_period: Optional. Period in seconds during which the location can be updated, should be between 60 and 86400, or 0x7FFFFFFF for live locations that can be edited indefinitely. :type live_period: :obj:`int` - :param heading: Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 - and 360 if specified. + :param heading: Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. :type heading: :obj:`int` - :param proximity_alert_radius: Optional. For live locations, a maximum distance for proximity alerts about - approaching another chat member, in meters. Must be between 1 and 100000 if specified. + :param proximity_alert_radius: Optional. For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. :type proximity_alert_radius: :obj:`int` :return: Instance of the class @@ -4999,16 +4996,13 @@ class InlineQueryResultLocation(InlineQueryResultBase): :param horizontal_accuracy: Optional. The radius of uncertainty for the location, measured in meters; 0-1500 :type horizontal_accuracy: :obj:`float` number - :param live_period: Optional. Period in seconds for which the location can be updated, should be between 60 and - 86400. + :param live_period: Optional. Period in seconds during which the location can be updated, should be between 60 and 86400, or 0x7FFFFFFF for live locations that can be edited indefinitely. :type live_period: :obj:`int` - :param heading: Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 - and 360 if specified. + :param heading: Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. :type heading: :obj:`int` - :param proximity_alert_radius: Optional. For live locations, a maximum distance for proximity alerts about - approaching another chat member, in meters. Must be between 1 and 100000 if specified. + :param proximity_alert_radius: Optional. For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. :type proximity_alert_radius: :obj:`int` :param reply_markup: Optional. Inline keyboard attached to the message From 51db7de19c24d31090b38a1a6d4228faeae25fd9 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 May 2024 12:27:17 +0300 Subject: [PATCH 05/16] Added the parameter live_period to the method editMessageLiveLocation. --- telebot/__init__.py | 9 +++++++-- telebot/apihelper.py | 7 ++++--- telebot/async_telebot.py | 10 ++++++++-- telebot/asyncio_helper.py | 9 +++++---- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 15bfd5dc0..10c8302bc 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3243,7 +3243,9 @@ def edit_message_live_location( timeout: Optional[int]=None, horizontal_accuracy: Optional[float]=None, heading: Optional[int]=None, - proximity_alert_radius: Optional[int]=None) -> types.Message or bool: + proximity_alert_radius: Optional[int]=None, + live_period: Optional[int]=None, + ) -> types.Message or bool: """ Use this method to edit live location messages. A location can be edited until its live_period expires or editing is explicitly disabled by a call to stopMessageLiveLocation. On success, if the edited message is not an inline message, the edited Message @@ -3282,6 +3284,9 @@ def edit_message_live_location( :param proximity_alert_radius: The maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. :type proximity_alert_radius: :obj:`int` + :param live_period: The maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. + :type live_period: :obj:`int` + :return: On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned. :rtype: :class:`telebot.types.Message` or bool """ @@ -3289,7 +3294,7 @@ def edit_message_live_location( apihelper.edit_message_live_location( self.token, latitude, longitude, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, reply_markup=reply_markup, timeout=timeout, horizontal_accuracy=horizontal_accuracy, heading=heading, - proximity_alert_radius=proximity_alert_radius) + proximity_alert_radius=proximity_alert_radius, live_period=live_period) ) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 8941b88ae..0dd7e54d6 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -572,9 +572,8 @@ def send_location( def edit_message_live_location( - token, latitude, longitude, chat_id=None, message_id=None, - inline_message_id=None, reply_markup=None, timeout=None, - horizontal_accuracy=None, heading=None, proximity_alert_radius=None): + token, latitude, longitude, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None, + timeout=None, horizontal_accuracy=None, heading=None, proximity_alert_radius=None, live_period=None): method_url = r'editMessageLiveLocation' payload = {'latitude': latitude, 'longitude': longitude} if chat_id: @@ -587,6 +586,8 @@ def edit_message_live_location( payload['heading'] = heading if proximity_alert_radius: payload['proximity_alert_radius'] = proximity_alert_radius + if live_period: + payload['live_period'] = live_period if inline_message_id: payload['inline_message_id'] = inline_message_id if reply_markup: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 435f74428..f35f092fb 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4655,7 +4655,9 @@ async def edit_message_live_location( timeout: Optional[int]=None, horizontal_accuracy: Optional[float]=None, heading: Optional[int]=None, - proximity_alert_radius: Optional[int]=None) -> types.Message: + proximity_alert_radius: Optional[int]=None, + live_period: Optional[int]=None, + ) -> types.Message: """ Use this method to edit live location messages. A location can be edited until its live_period expires or editing is explicitly disabled by a call to stopMessageLiveLocation. On success, if the edited message is not an inline message, the edited Message @@ -4694,6 +4696,9 @@ async def edit_message_live_location( :param proximity_alert_radius: The maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. :type proximity_alert_radius: :obj:`int` + :param live_period: The maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. + :type live_period: :obj:`int` + :return: On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned. :rtype: :class:`telebot.types.Message` or bool """ @@ -4701,7 +4706,8 @@ async def edit_message_live_location( await asyncio_helper.edit_message_live_location( self.token, latitude, longitude, chat_id, message_id, inline_message_id, reply_markup, timeout, - horizontal_accuracy, heading, proximity_alert_radius)) + horizontal_accuracy, heading, proximity_alert_radius, live_period=live_period) + ) async def stop_message_live_location( self, chat_id: Optional[Union[int, str]]=None, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 9c8f6128c..142491a3e 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -564,9 +564,8 @@ async def send_location( async def edit_message_live_location( - token, latitude, longitude, chat_id=None, message_id=None, - inline_message_id=None, reply_markup=None, timeout=None, - horizontal_accuracy=None, heading=None, proximity_alert_radius=None): + token, latitude, longitude, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None, + timeout=None, horizontal_accuracy=None, heading=None, proximity_alert_radius=None, live_period=None): method_url = r'editMessageLiveLocation' payload = {'latitude': latitude, 'longitude': longitude} if chat_id: @@ -579,8 +578,10 @@ async def edit_message_live_location( payload['heading'] = heading if proximity_alert_radius: payload['proximity_alert_radius'] = proximity_alert_radius + if live_period: + payload['live_period'] = live_period if inline_message_id: - payload['inline_message_id'] = inline_message_id + payload['inline_message_id'] = inline_message_id\ if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if timeout: From 71346d8701265f8e952e87d515d258c1d4b8f5a2 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 May 2024 12:31:02 +0300 Subject: [PATCH 06/16] Added the parameter live_period to the method editMessageLiveLocation u1 --- telebot/__init__.py | 2 +- telebot/async_telebot.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 10c8302bc..c82a23c37 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3284,7 +3284,7 @@ def edit_message_live_location( :param proximity_alert_radius: The maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. :type proximity_alert_radius: :obj:`int` - :param live_period: The maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. + :param live_period: New period in seconds during which the location can be updated, starting from the message send date. If 0x7FFFFFFF is specified, then the location can be updated forever. Otherwise, the new value must not exceed the current live_period by more than a day, and the live location expiration date must remain within the next 90 days. If not specified, then live_period remains unchanged :type live_period: :obj:`int` :return: On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned. diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index f35f092fb..8968a3c0f 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4696,7 +4696,7 @@ async def edit_message_live_location( :param proximity_alert_radius: The maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. :type proximity_alert_radius: :obj:`int` - :param live_period: The maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. + :param live_period: New period in seconds during which the location can be updated, starting from the message send date. If 0x7FFFFFFF is specified, then the location can be updated forever. Otherwise, the new value must not exceed the current live_period by more than a day, and the live location expiration date must remain within the next 90 days. If not specified, then live_period remains unchanged :type live_period: :obj:`int` :return: On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned. From 1a562e566f98326a6a08a08b53bdc996ba1a35c2 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 May 2024 12:38:46 +0300 Subject: [PATCH 07/16] Added the field question_entities to the class Poll. --- telebot/types.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 0ae4c53c0..aa0026558 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6916,16 +6916,13 @@ class Poll(JsonDeserializable): :param allows_multiple_answers: True, if the poll allows multiple answers :type allows_multiple_answers: :obj:`bool` - :param correct_option_id: Optional. 0-based identifier of the correct answer option. Available only for polls in - the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot. + :param correct_option_id: Optional. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot. :type correct_option_id: :obj:`int` - :param explanation: Optional. Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a - quiz-style poll, 0-200 characters + :param explanation: Optional. Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters :type explanation: :obj:`str` - :param explanation_entities: Optional. Special entities like usernames, URLs, bot commands, etc. that appear in - the explanation + :param explanation_entities: Optional. Special entities like usernames, URLs, bot commands, etc. that appear in the explanation :type explanation_entities: :obj:`list` of :class:`telebot.types.MessageEntity` :param open_period: Optional. Amount of time in seconds the poll will be active after creation @@ -6934,6 +6931,9 @@ class Poll(JsonDeserializable): :param close_date: Optional. Point in time (Unix timestamp) when the poll will be automatically closed :type close_date: :obj:`int` + :param question_entities: Optional. Special entities that appear in the question. Currently, only custom emoji entities are allowed in poll questions + :type question_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + :return: Instance of the class :rtype: :class:`telebot.types.Poll` """ @@ -6948,6 +6948,8 @@ def de_json(cls, json_string): obj['options'] = options or None if 'explanation_entities' in obj: obj['explanation_entities'] = Message.parse_entities(obj['explanation_entities']) + if 'question_entities' in obj: + obj['question_entities'] = Message.parse_entities(obj['question_entities']) return cls(**obj) # noinspection PyShadowingBuiltins @@ -6956,7 +6958,8 @@ def __init__( question, options, poll_id=None, total_voter_count=None, is_closed=None, is_anonymous=None, type=None, allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_entities=None, - open_period=None, close_date=None, poll_type=None, **kwargs): + open_period=None, close_date=None, poll_type=None, question_entities=None, + **kwargs): self.id: str = poll_id self.question: str = question self.options: List[PollOption] = options @@ -6972,6 +6975,7 @@ def __init__( self.correct_option_id: int = correct_option_id self.explanation: str = explanation self.explanation_entities: List[MessageEntity] = explanation_entities + self.question_entities: List[MessageEntity] = question_entities self.open_period: int = open_period self.close_date: int = close_date From 4bee6ffbda67fcbe1daba951016180a581eed343 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 May 2024 12:48:50 +0300 Subject: [PATCH 08/16] Added the field questiAdded the field question_entities to the class Poll.on_entities to the class Poll. --- telebot/types.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index aa0026558..3b350dbfc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6868,6 +6868,9 @@ class PollOption(JsonDeserializable): :param voter_count: Number of users that voted for this option :type voter_count: :obj:`int` + :param text_entities: Optional. Special entities that appear in the option text. Currently, only custom emoji entities are allowed in poll option texts + :type text_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + :return: Instance of the class :rtype: :class:`telebot.types.PollOption` """ @@ -6875,11 +6878,14 @@ class PollOption(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string, dict_copy=False) + if 'text_entities' in obj: + obj['text_entities'] = Message.parse_entities(obj['text_entities']) return cls(**obj) - def __init__(self, text, voter_count = 0, **kwargs): + def __init__(self, text, voter_count = 0, text_entities=None, **kwargs): self.text: str = text self.voter_count: int = voter_count + self.text_entities: List[MessageEntity] = text_entities # Converted in _convert_poll_options # def to_json(self): # # send_poll Option is a simple string: https://core.telegram.org/bots/api#sendpoll From 9c64ad2345aa6ff4a480ea63a732e11debfa1988 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 May 2024 13:04:51 +0300 Subject: [PATCH 09/16] Added the parameters question_parse_mode and question_entities to the method sendPoll. --- telebot/__init__.py | 27 +++++++++++++++++---------- telebot/apihelper.py | 27 +++++++++++++++------------ telebot/async_telebot.py | 15 +++++++++++++-- telebot/asyncio_helper.py | 8 ++++++-- 4 files changed, 51 insertions(+), 26 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index c82a23c37..b76dbccce 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5150,7 +5150,10 @@ def send_poll( protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + question_parse_mode: Optional[str] = None, + question_entities: Optional[List[types.MessageEntity]] = None, + ) -> types.Message: """ Use this method to send a native poll. On success, the sent Message is returned. @@ -5175,12 +5178,10 @@ def send_poll( :param allows_multiple_answers: True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to False :type allows_multiple_answers: :obj:`bool` - :param correct_option_id: 0-based identifier of the correct answer option. Available only for polls in quiz mode, - defaults to None + :param correct_option_id: 0-based identifier of the correct answer option. Available only for polls in quiz mode, defaults to None :type correct_option_id: :obj:`int` - :param explanation: Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, - 0-200 characters with at most 2 line feeds after entities parsing + :param explanation: Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing :type explanation: :obj:`str` :param explanation_parse_mode: Mode for parsing entities in the explanation. See formatting options for more details. @@ -5204,15 +5205,13 @@ def send_poll( :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found :type allow_sending_without_reply: :obj:`bool` - :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. + :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. :type reply_markup: :obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply` :param timeout: Timeout in seconds for waiting for a response from the user. :type timeout: :obj:`int` - :param explanation_entities: A JSON-serialized list of special entities that appear in the explanation, - which can be specified instead of parse_mode + :param explanation_entities: A JSON-serialized list of special entities that appear in the explanation, which can be specified instead of parse_mode :type explanation_entities: :obj:`list` of :obj:`MessageEntity` :param protect_content: Protects the contents of the sent message from forwarding and saving @@ -5227,6 +5226,12 @@ def send_poll( :param business_connection_id: Identifier of the business connection to use for the poll :type business_connection_id: :obj:`str` + :param question_parse_mode: Mode for parsing entities in the question. See formatting options for more details. Currently, only custom emoji entities are allowed + :type question_parse_mode: :obj:`str` + + :param question_entities: A JSON-serialized list of special entities that appear in the poll question. It can be specified instead of question_parse_mode + :type question_entities: :obj:`list` of :obj:`MessageEntity` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -5258,6 +5263,7 @@ def send_poll( raise RuntimeError("The send_poll signature was changed, please see send_poll function details.") explanation_parse_mode = self.parse_mode if (explanation_parse_mode is None) else explanation_parse_mode + question_parse_mode = self.parse_mode if (question_parse_mode is None) else question_parse_mode return types.Message.de_json( apihelper.send_poll( @@ -5268,7 +5274,8 @@ def send_poll( close_date=close_date, is_closed=is_closed, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, explanation_entities=explanation_entities, protect_content=protect_content, message_thread_id=message_thread_id, - reply_parameters=reply_parameters, business_connection_id=business_connection_id) + reply_parameters=reply_parameters, business_connection_id=business_connection_id, + question_parse_mode=question_parse_mode, question_entities=question_entities) ) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 0dd7e54d6..5a22d5f5a 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1818,8 +1818,8 @@ def send_poll( question, options, is_anonymous = None, type = None, allows_multiple_answers = None, correct_option_id = None, explanation = None, explanation_parse_mode=None, open_period = None, close_date = None, is_closed = None, disable_notification=False, - reply_markup=None, timeout=None, explanation_entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, - business_connection_id=None): + reply_markup=None, timeout=None, explanation_entities=None, protect_content=None, message_thread_id=None, + reply_parameters=None, business_connection_id=None, question_parse_mode=None, question_entities=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -1828,19 +1828,19 @@ def send_poll( if is_anonymous is not None: payload['is_anonymous'] = is_anonymous - if type is not None: + if type: payload['type'] = type if allows_multiple_answers is not None: payload['allows_multiple_answers'] = allows_multiple_answers if correct_option_id is not None: payload['correct_option_id'] = correct_option_id - if explanation is not None: + if explanation: payload['explanation'] = explanation - if explanation_parse_mode is not None: + if explanation_parse_mode: payload['explanation_parse_mode'] = explanation_parse_mode - if open_period is not None: + if open_period: payload['open_period'] = open_period - if close_date is not None: + if close_date: if isinstance(close_date, datetime): payload['close_date'] = close_date.timestamp() else: @@ -1849,21 +1849,24 @@ def send_poll( payload['is_closed'] = is_closed if disable_notification: payload['disable_notification'] = disable_notification - if reply_markup is not None: + if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout if explanation_entities: - payload['explanation_entities'] = json.dumps( - types.MessageEntity.to_list_of_dicts(explanation_entities)) + payload['explanation_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(explanation_entities)) if protect_content: payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id - if reply_parameters is not None: + if reply_parameters: payload['reply_parameters'] = reply_parameters.to_json() - if business_connection_id is not None: + if business_connection_id: payload['business_connection_id'] = business_connection_id + if question_parse_mode: + payload['question_parse_mode'] = question_parse_mode + if question_entities: + payload['question_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(question_entities)) return _make_request(token, method_url, params=payload) def create_forum_topic(token, chat_id, name, icon_color=None, icon_custom_emoji_id=None): diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 8968a3c0f..fcbd2fc1a 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6495,7 +6495,10 @@ async def send_poll( protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + question_parse_mode: Optional[str] = None, + question_entities: Optional[List[types.MessageEntity]] = None, + ) -> types.Message: """ Use this method to send a native poll. On success, the sent Message is returned. @@ -6572,6 +6575,12 @@ async def send_poll( :param business_connection_id: Identifier of the business connection to send the message through :type business_connection_id: :obj:`str` + :param question_parse_mode: Mode for parsing entities in the question. See formatting options for more details. Currently, only custom emoji entities are allowed + :type question_parse_mode: :obj:`str` + + :param question_entities: A JSON-serialized list of special entities that appear in the poll question. It can be specified instead of question_parse_mode + :type question_entities: :obj:`list` of :obj:`MessageEntity` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -6579,6 +6588,7 @@ async def send_poll( protect_content = self.protect_content if (protect_content is None) else protect_content explanation_parse_mode = self.parse_mode if (explanation_parse_mode is None) else explanation_parse_mode + question_parse_mode = self.parse_mode if (question_parse_mode is None) else question_parse_mode if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") @@ -6610,7 +6620,8 @@ async def send_poll( is_anonymous, type, allows_multiple_answers, correct_option_id, explanation, explanation_parse_mode, open_period, close_date, is_closed, disable_notification, - reply_markup, timeout, explanation_entities, protect_content, message_thread_id, reply_parameters, business_connection_id)) + reply_markup, timeout, explanation_entities, protect_content, message_thread_id, reply_parameters, + business_connection_id, question_parse_mode=question_parse_mode, question_entities=question_entities)) async def stop_poll( self, chat_id: Union[int, str], message_id: int, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 142491a3e..572140057 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1803,7 +1803,8 @@ async def send_poll( is_anonymous = None, type = None, allows_multiple_answers = None, correct_option_id = None, explanation = None, explanation_parse_mode=None, open_period = None, close_date = None, is_closed = None, disable_notification=False, - reply_markup=None, timeout=None, explanation_entities=None, protect_content=None, message_thread_id=None,reply_parameters=None,business_connection_id=None): + reply_markup=None, timeout=None, explanation_entities=None, protect_content=None, message_thread_id=None, + reply_parameters=None,business_connection_id=None, question_parse_mode=None, question_entities=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -1831,7 +1832,6 @@ async def send_poll( payload['close_date'] = close_date if is_closed is not None: payload['is_closed'] = is_closed - if disable_notification: payload['disable_notification'] = disable_notification if reply_parameters is not None: @@ -1849,6 +1849,10 @@ async def send_poll( payload['message_thread_id'] = message_thread_id if business_connection_id: payload['business_connection_id'] = business_connection_id + if question_parse_mode: + payload['question_parse_mode'] = question_parse_mode + if question_entities: + payload['question_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(question_entities)) return await _process_request(token, method_url, params=payload) From 0e45ef5a88925de0d6d8f8803bdf3bb6f3db69cb Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 May 2024 13:14:43 +0300 Subject: [PATCH 10/16] Added the parameters question_parse_mode and question_entities to the method sendPoll. u1 --- telebot/asyncio_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 572140057..7da6d7355 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -581,7 +581,7 @@ async def edit_message_live_location( if live_period: payload['live_period'] = live_period if inline_message_id: - payload['inline_message_id'] = inline_message_id\ + payload['inline_message_id'] = inline_message_id if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if timeout: From 2c9aa1d9382f6ccc92923ec6f864de8ad054f43a Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 May 2024 14:13:24 +0300 Subject: [PATCH 11/16] Added the class InputPollOption and changed the type of the parameter options in the method sendPoll to Array of InputPollOption. --- telebot/__init__.py | 21 +++++++++++++++++---- telebot/apihelper.py | 22 +++++----------------- telebot/async_telebot.py | 17 ++++++++++++++--- telebot/asyncio_helper.py | 20 +++----------------- telebot/types.py | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 41 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index b76dbccce..5035689aa 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -189,6 +189,7 @@ def __init__( # logs-related if colorful_logs: try: + # noinspection PyPackageRequirements import coloredlogs coloredlogs.install(logger=logger, level=logger.level) except ImportError: @@ -1012,6 +1013,7 @@ def __notify_update(self, new_messages): def _setup_change_detector(self, path_to_watch: str): try: + # noinspection PyPackageRequirements from watchdog.observers import Observer from telebot.ext.reloader import EventHandler except ImportError: @@ -5132,7 +5134,7 @@ def create_invoice_link(self, # noinspection PyShadowingBuiltins def send_poll( - self, chat_id: Union[int, str], question: str, options: List[str], + self, chat_id: Union[int, str], question: str, options: List[types.InputPollOption], is_anonymous: Optional[bool]=None, type: Optional[str]=None, allows_multiple_answers: Optional[bool]=None, correct_option_id: Optional[int]=None, @@ -5166,8 +5168,8 @@ def send_poll( :param question: Poll question, 1-300 characters :type question: :obj:`str` - :param options: A JSON-serialized list of answer options, 2-10 strings 1-100 characters each - :type options: :obj:`list` of :obj:`str` + :param options: A JSON-serialized list of 2-10 answer options + :type options: :obj:`list` of :obj:`InputPollOption` :param is_anonymous: True, if the poll needs to be anonymous, defaults to True :type is_anonymous: :obj:`bool` @@ -5265,6 +5267,17 @@ def send_poll( explanation_parse_mode = self.parse_mode if (explanation_parse_mode is None) else explanation_parse_mode question_parse_mode = self.parse_mode if (question_parse_mode is None) else question_parse_mode + if options and (not isinstance(options[0], types.InputPollOption)): + # show a deprecation warning + logger.warning("The parameter 'options' changed, should be List[types.InputPollOption], other types are deprecated.") + # convert options to appropriate type + if isinstance(options[0], str): + options = [types.InputPollOption(option) for option in options] + elif isinstance(options[0], types.PollOption): + options = [types.InputPollOption(option.text, text_entities=option.text_entities) for option in options] + else: + raise RuntimeError("Type of 'options' items is unknown. Options should be List[types.InputPollOption], other types are deprecated.") + return types.Message.de_json( apihelper.send_poll( self.token, chat_id, question, options, @@ -5574,7 +5587,7 @@ def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> types. apihelper.get_user_chat_boosts(self.token, chat_id, user_id) ) - + # noinspection PyShadowingBuiltins def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Union[Any, str]=None, format: Optional[str]=None) -> bool: """ Use this method to set the thumbnail of a sticker set. diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 5a22d5f5a..6b63c0b80 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -3,6 +3,7 @@ from datetime import datetime try: + # noinspection PyPackageRequirements import ujson as json except ImportError: import json @@ -371,6 +372,7 @@ def get_chat_member_count(token, chat_id): return _make_request(token, method_url, params=payload) +# noinspection PyShadowingBuiltins def set_sticker_set_thumbnail(token, name, user_id, thumbnail, format): method_url = r'setStickerSetThumbnail' payload = {'name': name, 'user_id': user_id, 'format': format} @@ -1814,8 +1816,7 @@ def create_invoice_link(token, title, description, payload, provider_token, # noinspection PyShadowingBuiltins def send_poll( - token, chat_id, - question, options, + token, chat_id, question, options, is_anonymous = None, type = None, allows_multiple_answers = None, correct_option_id = None, explanation = None, explanation_parse_mode=None, open_period = None, close_date = None, is_closed = None, disable_notification=False, reply_markup=None, timeout=None, explanation_entities=None, protect_content=None, message_thread_id=None, @@ -1824,7 +1825,8 @@ def send_poll( payload = { 'chat_id': str(chat_id), 'question': question, - 'options': json.dumps(_convert_poll_options(options))} + 'options': json.dumps([option.to_dict() for option in options]) + } if is_anonymous is not None: payload['is_anonymous'] = is_anonymous @@ -2002,20 +2004,6 @@ def _convert_markup(markup): return markup -def _convert_poll_options(poll_options): - if poll_options is None: - return None - elif len(poll_options) == 0: - return [] - elif isinstance(poll_options[0], str): - # Compatibility mode with previous bug when only list of string was accepted as poll_options - return poll_options - elif isinstance(poll_options[0], types.PollOption): - return [option.text for option in poll_options] - else: - return poll_options - - def convert_input_media(media): if isinstance(media, types.InputMedia): return media.convert_input_media() diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index fcbd2fc1a..23cfbdd48 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6477,7 +6477,7 @@ async def create_invoice_link(self, # noinspection PyShadowingBuiltins async def send_poll( - self, chat_id: Union[int, str], question: str, options: List[str], + self, chat_id: Union[int, str], question: str, options: List[InputPollOption], is_anonymous: Optional[bool]=None, type: Optional[str]=None, allows_multiple_answers: Optional[bool]=None, correct_option_id: Optional[int]=None, @@ -6511,8 +6511,8 @@ async def send_poll( :param question: Poll question, 1-300 characters :type question: :obj:`str` - :param options: A JSON-serialized list of answer options, 2-10 strings 1-100 characters each - :type options: :obj:`list` of :obj:`str` + :param options: A JSON-serialized list of 2-10 answer options + :type options: :obj:`list` of :obj:`InputPollOption` :param is_anonymous: True, if the poll needs to be anonymous, defaults to True :type is_anonymous: :obj:`bool` @@ -6613,6 +6613,17 @@ async def send_poll( if isinstance(question, types.Poll): raise RuntimeError("The send_poll signature was changed, please see send_poll function details.") + if options and (not isinstance(options[0], types.InputPollOption)): + # show a deprecation warning + logger.warning("The parameter 'options' changed, should be List[types.InputPollOption], other types are deprecated.") + # convert options to appropriate type + if isinstance(options[0], str): + options = [types.InputPollOption(option) for option in options] + elif isinstance(options[0], types.PollOption): + options = [types.InputPollOption(option.text, text_entities=option.text_entities) for option in options] + else: + raise RuntimeError("Type of 'options' items is unknown. Options should be List[types.InputPollOption], other types are deprecated.") + return types.Message.de_json( await asyncio_helper.send_poll( self.token, chat_id, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 7da6d7355..1a56348cf 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1798,8 +1798,7 @@ async def create_invoice_link(token, title, description, payload, provider_token # noinspection PyShadowingBuiltins async def send_poll( - token, chat_id, - question, options, + token, chat_id, question, options, is_anonymous = None, type = None, allows_multiple_answers = None, correct_option_id = None, explanation = None, explanation_parse_mode=None, open_period = None, close_date = None, is_closed = None, disable_notification=False, @@ -1809,7 +1808,8 @@ async def send_poll( payload = { 'chat_id': str(chat_id), 'question': question, - 'options': json.dumps(await _convert_poll_options(options))} + 'options': json.dumps([option.to_dict() for option in options]) + } if is_anonymous is not None: payload['is_anonymous'] = is_anonymous @@ -1980,20 +1980,6 @@ async def _convert_list_json_serializable(results): return '[' + ret + ']' -async def _convert_poll_options(poll_options): - if poll_options is None: - return None - elif len(poll_options) == 0: - return [] - elif isinstance(poll_options[0], str): - # Compatibility mode with previous bug when only list of string was accepted as poll_options - return poll_options - elif isinstance(poll_options[0], types.PollOption): - return [option.text for option in poll_options] - else: - return poll_options - - async def convert_input_media(media): if isinstance(media, types.InputMedia): return media.convert_input_media() diff --git a/telebot/types.py b/telebot/types.py index 3b350dbfc..d88de77c5 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6892,6 +6892,43 @@ def __init__(self, text, voter_count = 0, text_entities=None, **kwargs): # return json.dumps(self.text) +class InputPollOption(JsonSerializable): + """ + This object contains information about one answer option in a poll to send. + + Telegram Documentation: https://core.telegram.org/bots/api#inputpolloption + + :param text: Option text, 1-100 characters + :type text: :obj:`str` + + :param text_parse_mode: Optional. Mode for parsing entities in the text. See formatting options for more details. Currently, only custom emoji entities are allowed + :type text_parse_mode: :obj:`str` + + :param text_entities: Optional. A JSON-serialized list of special entities that appear in the poll option text. It can be specified instead of text_parse_mode + :type text_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :return: Instance of the class + :rtype: :class:`telebot.types.PollOption` + """ + def __init__(self, text, text_parse_mode=None, text_entities=None, **kwargs): + self.text: str = text + self.text_parse_mode: Optional[str] = text_parse_mode + self.text_entities: List[MessageEntity] = text_entities + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = { + "text": self.text, + } + if self.text_parse_mode: + json_dict["text_parse_mode"] = self.text_parse_mode + if self.text_entities: + json_dict['text_entities'] = [entity.to_dict() for entity in self.text_entities] + return json_dict + + class Poll(JsonDeserializable): """ This object contains information about a poll. From cad5025ad517affa8731512f418df47765b1d9f1 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 May 2024 14:14:50 +0300 Subject: [PATCH 12/16] Fix copy_message (not for BotAPI 7.3) --- telebot/__init__.py | 2 +- telebot/apihelper.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 5035689aa..1586fa5d4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1891,7 +1891,7 @@ def copy_message( apihelper.copy_message(self.token, chat_id, from_chat_id, message_id, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, - message_thread_id=message_thread_id, reply_parameters=reply_parameters)) + message_thread_id=message_thread_id, reply_parameters=reply_parameters)) def delete_message(self, chat_id: Union[int, str], message_id: int, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 6b63c0b80..6d113a339 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -439,6 +439,8 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m payload['disable_notification'] = disable_notification if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if reply_markup is not None: + payload['reply_markup'] = await _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout if protect_content is not None: From f75814bd5e3c6f343e4473a6525223eaa820bbf8 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 May 2024 14:16:22 +0300 Subject: [PATCH 13/16] Fix copy_message (not for BotAPI 7.3) u1 --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 6d113a339..ced5ee0b3 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -440,7 +440,7 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() if reply_markup is not None: - payload['reply_markup'] = await _convert_markup(reply_markup) + payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout if protect_content is not None: From 25241c4ecef74672f9b1937192082b29181b8ecc Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 11 May 2024 01:00:28 +0300 Subject: [PATCH 14/16] Added the classes ChatBackground, BackgroundType, BackgroundFill and the field chat_background_set of type ChatBackground to the class Message, describing service messages about background changes. --- telebot/types.py | 352 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 340 insertions(+), 12 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index d88de77c5..ea208c5ef 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -9,6 +9,7 @@ from abc import ABC try: + # noinspection PyPackageRequirements import ujson as json except ImportError: import json @@ -190,7 +191,7 @@ class Update(JsonDeserializable): :type edited_business_message: :class:`telebot.types.Message` :param deleted_business_messages: Optional. Service message: the chat connected to the business account was deleted - :type deleted_business_messages: :class:`telebot.types.Message` + :type deleted_business_messages: :class:`telebot.types.BusinessMessagesDeleted` :return: Instance of the class :rtype: :class:`telebot.types.Update` @@ -226,13 +227,15 @@ def de_json(cls, json_string): return cls(update_id, message, edited_message, channel_post, edited_channel_post, inline_query, chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer, - my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, removed_chat_boost, chat_boost, - business_connection, business_message, edited_business_message, deleted_business_messages) + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, + removed_chat_boost, chat_boost, business_connection, business_message, edited_business_message, + deleted_business_messages) def __init__(self, update_id, message, edited_message, channel_post, edited_channel_post, inline_query, chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer, - my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, removed_chat_boost, chat_boost, - business_connection, business_message, edited_business_message, deleted_business_messages, **kwargs): + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, + removed_chat_boost, chat_boost, business_connection, business_message, edited_business_message, + deleted_business_messages, **kwargs): self.update_id = update_id self.message = message self.edited_message = edited_message @@ -500,7 +503,8 @@ def de_json(cls, json_string): obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) - def __init__(self, id, is_bot, first_name, last_name=None, username=None, language_code=None, + # noinspection PyShadowingBuiltins + def __init__(self, id, is_bot, first_name, last_name=None, username=None, language_code=None, can_join_groups=None, can_read_all_group_messages=None, supports_inline_queries=None, is_premium=None, added_to_attachment_menu=None, can_connect_to_business=None, **kwargs): self.id: int = id @@ -516,7 +520,6 @@ def __init__(self, id, is_bot, first_name, last_name=None, username=None, langua self.added_to_attachment_menu: bool = added_to_attachment_menu self.can_connect_to_business: bool = can_connect_to_business - @property def full_name(self): """ @@ -543,7 +546,6 @@ def to_dict(self): 'is_premium': self.is_premium, 'added_to_attachment_menu': self.added_to_attachment_menu, 'can_connect_to_business': self.can_connect_to_business} - class GroupChat(JsonDeserializable): @@ -1092,6 +1094,9 @@ class Message(JsonDeserializable): :param boost_added: Optional. Service message: user boosted the chat :type boost_added: :class:`telebot.types.ChatBoostAdded` + :param chat_background_set: Optional. Service message: chat background set + :type chat_background_set: :class:`telebot.types.ChatBackground` + :param forum_topic_created: Optional. Service message: forum topic created :type forum_topic_created: :class:`telebot.types.ForumTopicCreated` @@ -1137,8 +1142,7 @@ class Message(JsonDeserializable): :param web_app_data: Optional. Service message: data sent by a Web App :type web_app_data: :class:`telebot.types.WebAppData` - :param reply_markup: Optional. Inline keyboard attached to the message. login_url buttons are represented as - ordinary url buttons. + :param reply_markup: Optional. Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons. :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` :return: Instance of the class @@ -1303,6 +1307,9 @@ def de_json(cls, json_string): content_type = 'message_auto_delete_timer_changed' if 'reply_markup' in obj: opts['reply_markup'] = InlineKeyboardMarkup.de_json(obj['reply_markup']) + if 'chat_background_set' in obj: + opts['chat_background_set'] = ChatBackground.de_json(obj['chat_background_set']) + content_type = 'chat_background_set' if 'forum_topic_created' in obj: opts['forum_topic_created'] = ForumTopicCreated.de_json(obj['forum_topic_created']) content_type = 'forum_topic_created' @@ -1451,6 +1458,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.reply_markup: Optional[InlineKeyboardMarkup] = None self.message_thread_id: Optional[int] = None self.is_topic_message: Optional[bool] = None + self.chat_background_set: Optional[ChatBackground] = None self.forum_topic_created: Optional[ForumTopicCreated] = None self.forum_topic_closed: Optional[ForumTopicClosed] = None self.forum_topic_reopened: Optional[ForumTopicReopened] = None @@ -9368,7 +9376,7 @@ def __getattr__(self, item): 'group_chat_created', 'supergroup_chat_created', 'channel_chat_created', 'message_auto_delete_timer_changed', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', 'invoice', 'successful_payment', 'users_shared', 'chat_shared', 'connected_website', 'write_access_allowed', 'passport_data', - 'proximity_alert_triggered', 'forum_topic_created', 'forum_topic_edited', 'forum_topic_closed', + 'proximity_alert_triggered', 'chat_background_set', 'forum_topic_created', 'forum_topic_edited', 'forum_topic_closed', 'forum_topic_reopened', 'general_forum_topic_hidden', 'general_forum_topic_unhidden', 'giveaway_created', 'giveaway', 'giveaway_winners', 'giveaway_completed', 'video_chat_scheduled', 'video_chat_started', 'video_chat_ended', 'video_chat_participants_invited', 'web_app_data', 'reply_markup' @@ -9668,4 +9676,324 @@ def de_json(cls, json_string): def __init__(self, day, month, year=None, **kwargs): self.day: int = day self.month: int = month - self.year: Optional[int] = year \ No newline at end of file + self.year: Optional[int] = year + + +class BackgroundFill(ABC, JsonDeserializable): + """ + This object describes the way a background is filled based on the selected colors. Currently, it can be one of + BackgroundFillSolid + BackgroundFillGradient + BackgroundFillFreeformGradient + + Telegram documentation: https://core.telegram.org/bots/api#backgroundfill + + :return: Instance of the class + :rtype: :class:`BackgroundFillSolid` or :class:`BackgroundFillGradient` or :class:`BackgroundFillFreeformGradient` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if obj["type"] == "solid": + return BackgroundFillSolid.de_json(obj) + elif obj["type"] == "gradient": + return BackgroundFillGradient.de_json(obj) + elif obj["type"] == "freeform_gradient": + return BackgroundFillFreeformGradient.de_json(obj) + return None + + +# noinspection PyShadowingBuiltins +class BackgroundFillSolid(BackgroundFill): + """ + The background is filled using the selected color. + + Telegram documentation: https://core.telegram.org/bots/api#backgroundfillsolid + + :param type: Type of the background fill, always “solid” + :type type: :obj:`str` + + :param color: The color of the background fill in the RGB24 format + :type color: :class:`int` + + :return: Instance of the class + :rtype: :class:`BackgroundFillSolid` + """ + + @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, type, color, **kwargs): + self.type: str = type + self.color: int = color + + +# noinspection PyShadowingBuiltins +class BackgroundFillGradient(BackgroundFill): + """ + The background is a gradient fill. + + Telegram documentation: https://core.telegram.org/bots/api#backgroundfillgradient + + :param type: Type of the background fill, always “gradient” + :type type: :obj:`str` + + :param top_color: Top color of the gradient in the RGB24 format + :type top_color: :class:`int` + + :param bottom_color: Bottom color of the gradient in the RGB24 format + :type bottom_color: :class:`int` + + :param rotation_angle: Clockwise rotation angle of the background fill in degrees; 0-359 + :type rotation_angle: :class:`int` + + :return: Instance of the class + :rtype: :class:`BackgroundFillGradient` + """ + + @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, type, top_color, bottom_color, rotation_angle, **kwargs): + self.type: str = type + self.top_color: int = top_color + self.bottom_color: int = bottom_color + self.rotation_angle: int = rotation_angle + + +# noinspection PyShadowingBuiltins +class BackgroundFillFreeformGradient(BackgroundFill): + """ + The background is a freeform gradient that rotates after every message in the chat. + + Telegram documentation: https://core.telegram.org/bots/api#backgroundfillfreeformgradient + + :param type: Type of the background fill, always “freeform_gradient” + :type type: :obj:`str` + + :param colors: A list of the 3 or 4 base colors that are used to generate the freeform gradient in the RGB24 format + :type colors: :obj:`list` of :class:`int` + + :return: Instance of the class + :rtype: :class:`BackgroundFillFreeformGradient` + """ + + @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, type, colors, **kwargs): + self.type: str = type + self.colors: List[int] = colors + + +class BackgroundType(ABC, JsonDeserializable): + """ + This object describes the type of a background. Currently, it can be one of + BackgroundTypeFill + BackgroundTypeWallpaper + BackgroundTypePattern + BackgroundTypeChatTheme + + Telegram documentation: https://core.telegram.org/bots/api#backgroundtype + + :return: Instance of the class + :rtype: :class:`BackgroundTypeFill` or :class:`BackgroundTypeWallpaper` or :class:`BackgroundTypePattern` or :class:`BackgroundTypeChatTheme` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if obj["type"] == "fill": + return BackgroundTypeFill.de_json(obj) + elif obj["type"] == "wallpaper": + return BackgroundTypeWallpaper.de_json(obj) + elif obj["type"] == "pattern": + return BackgroundTypePattern.de_json(obj) + elif obj["type"] == "chat_theme": + return BackgroundTypeChatTheme.de_json(obj) + return None + + +# noinspection PyShadowingBuiltins +class BackgroundTypeFill(BackgroundFill): + """ + The background is automatically filled based on the selected colors. + + Telegram documentation: https://core.telegram.org/bots/api#backgroundtypefill + + :param type: Type of the background, always “fill” + :type type: :obj:`str` + + :param fill: The background fill + :type fill: :class:`BackgroundFill` + + :param dark_theme_dimming: Dimming of the background in dark themes, as a percentage; 0-100 + :type dark_theme_dimming: :obj:`int` + + :return: Instance of the class + :rtype: :class:`BackgroundTypeFill` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['fill'] = BackgroundFill.de_json(obj['fill']) + return cls(**obj) + + def __init__(self, type, fill, dark_theme_dimming, **kwargs): + self.type: str = type + self.fill: BackgroundFill = fill + self.dark_theme_dimming: int = dark_theme_dimming + + +# noinspection PyShadowingBuiltins +class BackgroundTypeWallpaper(BackgroundFill): + """ + The background is a wallpaper in the JPEG format. + + Telegram documentation: https://core.telegram.org/bots/api#backgroundtypewallpaper + + :param type: Type of the background, always “wallpaper” + :type type: :obj:`str` + + :param document: Document with the wallpaper + :type document: :class:`Document` + + :param dark_theme_dimming: Dimming of the background in dark themes, as a percentage; 0-100 + :type dark_theme_dimming: :obj:`int` + + :param is_blurred: Optional. True, if the wallpaper is downscaled to fit in a 450x450 square and then box-blurred with radius 12 + :type is_blurred: :obj:`bool` + + :param is_moving: Optional. True, if the background moves slightly when the device is tilted + :type is_moving: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`BackgroundTypeWallpaper` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['document'] = Document.de_json(obj['document']) + obj['fill'] = BackgroundFill.de_json(obj['fill']) + return cls(**obj) + + def __init__(self, type, document, dark_theme_dimming, is_blurred=None, is_moving=None, **kwargs): + self.type: str = type + self.document: Document = document + self.dark_theme_dimming: int = dark_theme_dimming + self.is_blurred: Optional[bool] = is_blurred + self.is_moving: Optional[bool] = is_moving + + +# noinspection PyShadowingBuiltins +class BackgroundTypePattern(BackgroundFill): + """ + The background is a wallpaper in the JPEG format. + + Telegram documentation: https://core.telegram.org/bots/api#backgroundtypepattern + + :param type: Type of the background, always “pattern” + :type type: :obj:`str` + + :param document: Document with the pattern + :type document: :class:`Document` + + :param fill: The background fill that is combined with the pattern + :type fill: :class:`BackgroundFill` + + :param intensity: Intensity of the pattern when it is shown above the filled background; 0-100 + :type intensity: :obj:`int` + + :param is_inverted: Optional. True, if the background fill must be applied only to the pattern itself. All other pixels are black in this case. For dark themes only + :type is_inverted: :obj:`bool` + + :param is_moving: Optional. True, if the background moves slightly when the device is tilted + :type is_moving: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`BackgroundTypePattern` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['document'] = Document.de_json(obj['document']) + return cls(**obj) + + def __init__(self, type, document, fill, intensity, is_inverted=None, is_moving=None, **kwargs): + self.type: str = type + self.document: Document = document + self.fill: BackgroundFill = fill + self.intensity: int = intensity + self.is_inverted: Optional[bool] = is_inverted + self.is_moving: Optional[bool] = is_moving + + +# noinspection PyShadowingBuiltins +class BackgroundTypeChatTheme(BackgroundFill): + """ + The background is taken directly from a built-in chat theme. + + Telegram documentation: https://core.telegram.org/bots/api#backgroundtypechattheme + + :param type: Type of the background, always “chat_theme” + :type type: :obj:`str` + + :param theme_name: Intensity of the pattern when it is shown above the filled background; 0-100 + :type theme_name: :obj:`str` + + :return: Instance of the class + :rtype: :class:`BackgroundTypeChatTheme` + """ + + @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, type, theme_name, **kwargs): + self.type: str = type + self.theme_name: str = theme_name + + +# noinspection PyShadowingBuiltins +class ChatBackground(JsonDeserializable): + """ + This object represents a chat background. + + Telegram documentation: https://core.telegram.org/bots/api#chatbackground + + :param type: Type of the background + :type type: :class:`BackgroundType` + + :return: Instance of the class + :rtype: :class:`ChatBackground` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['type'] = BackgroundType.de_json(obj['type']) + return cls(**obj) + + def __init__(self, type, **kwargs): + self.type: BackgroundType = type From 9a3d0488f3dc0c52e58ccac5de09ba8fd0e3bb0b Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 11 May 2024 01:09:42 +0300 Subject: [PATCH 15/16] Suppress some warnings (not for BotAPI 7.3) --- telebot/types.py | 78 +++++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index ea208c5ef..3c0edfe39 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -548,6 +548,7 @@ def to_dict(self): 'can_connect_to_business': self.can_connect_to_business} +# noinspection PyShadowingBuiltins class GroupChat(JsonDeserializable): """ :meta private: @@ -563,6 +564,7 @@ def __init__(self, id, title, **kwargs): self.title: str = title +# noinspection PyShadowingBuiltins class Chat(JsonDeserializable): """ This object represents a chat. @@ -1584,6 +1586,7 @@ def user_shared(self): return self.users_shared +# noinspection PyShadowingBuiltins class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): """ This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc. @@ -2461,20 +2464,20 @@ def to_json(self): return json.dumps(json_dict) +# noinspection PyShadowingBuiltins class KeyboardButtonPollType(Dictionaryable): """ This object represents type of a poll, which is allowed to be created and sent when the corresponding button is pressed. Telegram Documentation: https://core.telegram.org/bots/api#keyboardbuttonpolltype - :param type: Optional. If quiz is passed, the user will be allowed to create only polls in the quiz mode. If regular is - passed, only regular polls will be allowed. Otherwise, the user will be allowed to create a poll of any type. + :param type: Optional. If quiz is passed, the user will be allowed to create only polls in the quiz mode. If regular is passed, only regular polls will be allowed. Otherwise, the user will be allowed to create a poll of any type. :type type: :obj:`str` :return: Instance of the class :rtype: :class:`telebot.types.KeyboardButtonPollType` """ - def __init__(self, type=''): + def __init__(self, type=None): self.type: str = type def to_dict(self): @@ -2991,6 +2994,7 @@ def to_dict(self): return json_dict +# noinspection PyShadowingBuiltins class CallbackQuery(JsonDeserializable): """ This object represents an incoming callback query from a callback button in an inline keyboard. If the button that originated the query was attached to a message sent by the bot, the field message will be present. If the button was attached to a message sent via the bot (in inline mode), the field inline_message_id will be present. Exactly one of the fields data or game_short_name will be present. @@ -3585,6 +3589,7 @@ def to_dict(self): # BotCommandScopes +# noinspection PyShadowingBuiltins class BotCommandScope(ABC, JsonSerializable): """ This object represents the scope to which bot commands are applied. Currently, the following 7 scopes are supported: @@ -3790,6 +3795,7 @@ def __init__(self, chat_id=None, user_id=None): # InlineQuery +# noinspection PyShadowingBuiltins class InlineQuery(JsonDeserializable): """ This object represents an incoming inline query. When the user sends an empty query, your bot could return some default or trending results. @@ -4316,7 +4322,7 @@ def to_dict(self): return json_dict -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultArticle(InlineQueryResultBase): """ Represents a link to an article or web page. @@ -4401,7 +4407,7 @@ def to_dict(self): return json_dict -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultPhoto(InlineQueryResultBase): """ Represents a link to a photo. By default, this photo will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo. @@ -4481,7 +4487,7 @@ def to_dict(self): return json_dict -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultGif(InlineQueryResultBase): """ Represents a link to an animated GIF file. By default, this animated GIF file will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation. @@ -4574,7 +4580,7 @@ def to_dict(self): return json_dict -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultMpeg4Gif(InlineQueryResultBase): """ Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default, this animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation. @@ -4667,7 +4673,7 @@ def to_dict(self): return json_dict -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultVideo(InlineQueryResultBase): """ Represents a link to a page containing an embedded video player or a video file. By default, this video file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the video. @@ -4759,7 +4765,7 @@ def to_dict(self): return json_dict -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultAudio(InlineQueryResultBase): """ Represents a link to an MP3 audio file. By default, this audio file will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the audio. @@ -4824,7 +4830,7 @@ def to_dict(self): return json_dict -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultVoice(InlineQueryResultBase): """ Represents a link to a voice recording in an .OGG container encoded with OPUS. By default, this voice recording will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the the voice message. @@ -4882,7 +4888,7 @@ def to_dict(self): return json_dict -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultDocument(InlineQueryResultBase): """ Represents a link to a file. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file. Currently, only .PDF and .ZIP files can be sent using this method. @@ -4979,7 +4985,7 @@ def to_dict(self): return json_dict -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultLocation(InlineQueryResultBase): """ Represents a location on a map. By default, the location will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the location. @@ -5081,7 +5087,7 @@ def to_dict(self): return json_dict -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultVenue(InlineQueryResultBase): """ Represents a venue. By default, the venue will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the venue. @@ -5190,7 +5196,7 @@ def to_dict(self): return json_dict -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultContact(InlineQueryResultBase): """ Represents a contact with a phone number. By default, this contact will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the contact. @@ -5278,7 +5284,7 @@ def to_dict(self): return json_dict -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultGame(InlineQueryResultBase): """ Represents a Game. @@ -5347,7 +5353,7 @@ def to_json(self): return json.dumps(json_dict) -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultCachedPhoto(InlineQueryResultCachedBase): """ Represents a link to a photo stored on the Telegram servers. By default, this photo will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo. @@ -5406,7 +5412,7 @@ def __init__(self, id, photo_file_id, title=None, description=None, self.payload_dic['photo_file_id'] = photo_file_id -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultCachedGif(InlineQueryResultCachedBase): """ Represents a link to an animated GIF file stored on the Telegram servers. By default, this animated GIF file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with specified content instead of the animation. @@ -5461,7 +5467,7 @@ def __init__(self, id, gif_file_id, title=None, description=None, self.payload_dic['gif_file_id'] = gif_file_id -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultCachedMpeg4Gif(InlineQueryResultCachedBase): """ Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation. @@ -5516,7 +5522,7 @@ def __init__(self, id, mpeg4_file_id, title=None, description=None, self.payload_dic['mpeg4_file_id'] = mpeg4_file_id -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultCachedSticker(InlineQueryResultCachedBase): """ Represents a link to a sticker stored on the Telegram servers. By default, this sticker will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the sticker. @@ -5551,7 +5557,7 @@ def __init__(self, id, sticker_file_id, reply_markup=None, input_message_content self.payload_dic['sticker_file_id'] = sticker_file_id -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultCachedDocument(InlineQueryResultCachedBase): """ Represents a link to a file stored on the Telegram servers. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file. @@ -5610,7 +5616,7 @@ def __init__(self, id, document_file_id, title, description=None, self.payload_dic['document_file_id'] = document_file_id -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultCachedVideo(InlineQueryResultCachedBase): """ Represents a link to a video file stored on the Telegram servers. By default, this video file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the video. @@ -5670,7 +5676,7 @@ def __init__(self, id, video_file_id, title, description=None, self.payload_dic['video_file_id'] = video_file_id -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultCachedVoice(InlineQueryResultCachedBase): """ Represents a link to a voice message stored on the Telegram servers. By default, this voice message will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the voice message. @@ -5724,7 +5730,7 @@ def __init__(self, id, voice_file_id, title, caption=None, caption_entities = No self.payload_dic['voice_file_id'] = voice_file_id -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultCachedAudio(InlineQueryResultCachedBase): """ Represents a link to an MP3 audio file stored on the Telegram servers. By default, this audio file will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the audio. @@ -6092,7 +6098,7 @@ def __init__(self, name=None, phone_number=None, email=None, shipping_address=No self.shipping_address: ShippingAddress = shipping_address -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class ShippingOption(JsonSerializable): """ This object represents one shipping option. @@ -6187,6 +6193,7 @@ def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=N self.provider_payment_charge_id: str = provider_payment_charge_id +# noinspection PyShadowingBuiltins class ShippingQuery(JsonDeserializable): """ This object contains information about an incoming shipping query. @@ -6223,6 +6230,7 @@ def __init__(self, id, from_user, invoice_payload, shipping_address, **kwargs): self.shipping_address: ShippingAddress = shipping_address +# noinspection PyShadowingBuiltins class PreCheckoutQuery(JsonDeserializable): """ This object contains information about an incoming pre-checkout query. @@ -6350,6 +6358,7 @@ def is_video(self): return False +# noinspection PyShadowingBuiltins class Sticker(JsonDeserializable): """ This object represents a sticker. @@ -6494,6 +6503,7 @@ def to_dict(self): # InputMedia +# noinspection PyShadowingBuiltins class InputMedia(Dictionaryable, JsonSerializable): """ This object represents the content of a media message to be sent. It should be one of @@ -7398,12 +7408,12 @@ class MenuButton(JsonDeserializable, JsonSerializable, Dictionaryable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - map = { + types = { 'commands': MenuButtonCommands, 'web_app': MenuButtonWebApp, 'default': MenuButtonDefault } - return map[obj['type']](**obj) + return types[obj['type']](**obj) def to_json(self): """ @@ -7416,8 +7426,9 @@ def to_dict(self): :meta private: """ raise NotImplementedError - + +# noinspection PyUnusedLocal class MenuButtonCommands(MenuButton): """ Represents a menu button, which opens the bot's list of commands. @@ -7441,6 +7452,7 @@ def to_json(self): return json.dumps(self.to_dict()) +# noinspection PyUnusedLocal class MenuButtonWebApp(MenuButton): """ Represents a menu button, which launches a Web App. @@ -7472,7 +7484,8 @@ def to_dict(self): def to_json(self): return json.dumps(self.to_dict()) - + +# noinspection PyUnusedLocal class MenuButtonDefault(MenuButton): """ Describes that no specific value for the menu button was set. @@ -7953,6 +7966,7 @@ def __init__(self, short_description: str, **kwargs) -> None: self.short_description: str = short_description +# noinspection PyShadowingBuiltins class InputSticker(Dictionaryable, JsonSerializable): """ This object describes a sticker to be added to a sticker set. @@ -8187,6 +8201,7 @@ def __init__(self, chat: Chat, id: int, **kwargs) -> None: # base class +# noinspection PyShadowingBuiltins class ReactionType(JsonDeserializable, Dictionaryable, JsonSerializable): """ This object represents a reaction type. @@ -8371,8 +8386,9 @@ def __init__(self, chat: Chat, message_id: int, date: int, reactions: List[React self.message_id: int = message_id self.date: int = date self.reactions: List[ReactionCount] = reactions - + +# noinspection PyShadowingBuiltins class ReactionCount(JsonDeserializable): """ This object represents a reaction added to a message along with the number of times it was added. @@ -8563,7 +8579,7 @@ def __init__( self.venue: Optional[Venue] = venue -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyShadowingBuiltins class MessageOrigin(JsonDeserializable): """ This object describes the origin of a message. @@ -9409,7 +9425,7 @@ def __init__(self, boost_count, **kwargs): self.boost_count: int = boost_count - +# noinspection PyShadowingBuiltins class BusinessConnection(JsonDeserializable): """ This object describes the connection of the bot with a business account. From 3532812e0ed6d44675a4f16c4e2efd3c57b3dd1d Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 11 May 2024 15:43:05 +0300 Subject: [PATCH 16/16] Split out the class ChatFullInfo from the class Chat and changed the return type of the method getChat to ChatFullInfo. Added the field max_reaction_count to the class ChatFullInfo. --- telebot/__init__.py | 6 +-- telebot/apihelper.py | 4 +- telebot/async_telebot.py | 6 +-- telebot/types.py | 87 ++++++++++++++++++---------------------- 4 files changed, 48 insertions(+), 55 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 1586fa5d4..2d327ab5a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1493,7 +1493,7 @@ def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, ) - def get_chat(self, chat_id: Union[int, str]) -> types.Chat: + def get_chat(self, chat_id: Union[int, str]) -> types.ChatFullInfo: """ Use this method to get up to date information about the chat (current name of the user for one-on-one conversations, current username of a user, group or channel, etc.). Returns a Chat object on success. @@ -1504,9 +1504,9 @@ def get_chat(self, chat_id: Union[int, str]) -> types.Chat: :type chat_id: :obj:`int` or :obj:`str` :return: Chat information - :rtype: :class:`telebot.types.Chat` + :rtype: :class:`telebot.types.ChatFullInfo` """ - return types.Chat.de_json( + return types.ChatFullInfo.de_json( apihelper.get_chat(self.token, chat_id) ) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index ced5ee0b3..82a1e31e6 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1527,10 +1527,10 @@ def send_invoice( :param provider_data: A JSON-serialized data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider. :param timeout: :param max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency - :param suggested_tip_amounts: A JSON-serialized array of suggested amounts of tips in the smallest units of the currency. - At most 4 suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a strictly increased order and must not exceed max_tip_amount. + :param suggested_tip_amounts: A JSON-serialized array of suggested amounts of tips in the smallest units of the currency. At most 4 suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a strictly increased order and must not exceed max_tip_amount. :param protect_content: Protects the contents of the sent message from forwarding and saving :param message_thread_id: Unique identifier for the target message thread (topic) of the forum; for forum supergroups only + :param reply_parameters: A JSON-serialized object for an inline keyboard. If empty, one 'Pay total price' button will be shown. If not empty, the first button must be a Pay button. :return: """ method_url = r'sendInvoice' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 23cfbdd48..1b6c09413 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2889,7 +2889,7 @@ async def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None result = await asyncio_helper.get_user_profile_photos(self.token, user_id, offset, limit) return types.UserProfilePhotos.de_json(result) - async def get_chat(self, chat_id: Union[int, str]) -> types.Chat: + async def get_chat(self, chat_id: Union[int, str]) -> types.ChatFullInfo: """ Use this method to get up to date information about the chat (current name of the user for one-on-one conversations, current username of a user, group or channel, etc.). Returns a Chat object on success. @@ -2900,10 +2900,10 @@ async def get_chat(self, chat_id: Union[int, str]) -> types.Chat: :type chat_id: :obj:`int` or :obj:`str` :return: Chat information - :rtype: :class:`telebot.types.Chat` + :rtype: :class:`telebot.types.ChatFullInfo` """ result = await asyncio_helper.get_chat(self.token, chat_id) - return types.Chat.de_json(result) + return types.ChatFullInfo.de_json(result) async def leave_chat(self, chat_id: Union[int, str]) -> bool: """ diff --git a/telebot/types.py b/telebot/types.py index 3c0edfe39..cfbe447fd 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -565,7 +565,7 @@ def __init__(self, id, title, **kwargs): # noinspection PyShadowingBuiltins -class Chat(JsonDeserializable): +class ChatFullInfo(JsonDeserializable): """ This object represents a chat. @@ -594,11 +594,13 @@ class Chat(JsonDeserializable): :param is_forum: Optional. True, if the supergroup chat is a forum (has topics enabled) :type is_forum: :obj:`bool` + :param max_reaction_count: Optional. The maximum number of reactions that can be set on a message in the chat + :type max_reaction_count: :obj:`int` + :param photo: Optional. Chat photo. Returned only in getChat. :type photo: :class:`telebot.types.ChatPhoto` - :param active_usernames: Optional. If non-empty, the list of all active chat usernames; for private chats, supergroups and channels. - Returned only in getChat. + :param active_usernames: Optional. If non-empty, the list of all active chat usernames; for private chats, supergroups and channels. Returned only in getChat. :type active_usernames: :obj:`list` of :obj:`str` :param birthdate: Optional. Birthdate of the other party in a private chat. Returned only in getChat. @@ -616,100 +618,80 @@ class Chat(JsonDeserializable): :param personal_chat: Optional. For private chats, the personal channel of the user. Returned only in getChat. :type personal_chat: :class:`telebot.types.Chat` - :param available_reactions: Optional. List of available chat reactions; for private chats, supergroups and channels. - Returned only in getChat. + :param available_reactions: Optional. List of available chat reactions; for private chats, supergroups and channels. Returned only in getChat. :type available_reactions: :obj:`list` of :class:`telebot.types.ReactionType` :param accent_color_id: Optional. Optional. Identifier of the accent color for the chat name and backgrounds of the chat photo, reply header, and link preview. See accent colors for more details. Returned only in getChat. Always returned in getChat. :type accent_color_id: :obj:`int` - :param background_custom_emoji_id: Optional. Custom emoji identifier of emoji chosen by the chat for the reply header - and link preview background. Returned only in getChat. + :param background_custom_emoji_id: Optional. Custom emoji identifier of emoji chosen by the chat for the reply header and link preview background. Returned only in getChat. :type background_custom_emoji_id: :obj:`str` - :param profile_accent_color_id: Optional. Identifier of the accent color for the chat's profile background. - See profile accent colors for more details. Returned only in getChat. + :param profile_accent_color_id: Optional. Identifier of the accent color for the chat's profile background. See profile accent colors for more details. Returned only in getChat. :type profile_accent_color_id: :obj:`int` - :param profile_background_custom_emoji_id: Optional. Custom emoji identifier of the emoji chosen by the chat for its profile background. - Returned only in getChat. + :param profile_background_custom_emoji_id: Optional. Custom emoji identifier of the emoji chosen by the chat for its profile background. Returned only in getChat. :type profile_background_custom_emoji_id: :obj:`str` - :param emoji_status_custom_emoji_id: Optional. Custom emoji identifier of emoji status of the other party in a private chat. - Returned only in getChat. + :param emoji_status_custom_emoji_id: Optional. Custom emoji identifier of emoji status of the other party in a private chat. Returned only in getChat. :type emoji_status_custom_emoji_id: :obj:`str` - :param emoji_status_expiration_date: Optional. Expiration date of the emoji status of the other party in a private chat, - if any. Returned only in getChat. + :param emoji_status_expiration_date: Optional. Expiration date of the emoji status of the other party in a private chat, if any. Returned only in getChat. :type emoji_status_expiration_date: :obj:`int` :param bio: Optional. Bio of the other party in a private chat. Returned only in getChat. :type bio: :obj:`str` - :param has_private_forwards: Optional. :obj:`bool`, if privacy settings of the other party in the private chat - allows to use tg://user?id= links only in chats with the user. Returned only in getChat. + :param has_private_forwards: Optional. :obj:`bool`, if privacy settings of the other party in the private chat allows to use tg://user?id= links only in chats with the user. Returned only in getChat. :type has_private_forwards: :obj:`bool` - :param has_restricted_voice_and_video_messages: Optional. True, if the privacy settings of the other party restrict sending voice and video note messages - in the private chat. Returned only in getChat. + :param has_restricted_voice_and_video_messages: Optional. True, if the privacy settings of the other party restrict sending voice and video note messages in the private chat. Returned only in getChat. :type :obj:`bool` - :param join_to_send_messages: Optional. :obj:`bool`, if users need to join the supergroup before they can send - messages. Returned only in getChat. + :param join_to_send_messages: Optional. :obj:`bool`, if users need to join the supergroup before they can send messages. Returned only in getChat. :type join_to_send_messages: :obj:`bool` - :param join_by_request: Optional. :obj:`bool`, if all users directly joining the supergroup need to be approved - by supergroup administrators. Returned only in getChat. + :param join_by_request: Optional. :obj:`bool`, if all users directly joining the supergroup need to be approved by supergroup administrators. Returned only in getChat. :type join_by_request: :obj:`bool` :param description: Optional. Description, for groups, supergroups and channel chats. Returned only in getChat. :type description: :obj:`str` - :param invite_link: Optional. Primary invite link, for groups, supergroups and channel chats. Returned only in - getChat. + :param invite_link: Optional. Primary invite link, for groups, supergroups and channel chats. Returned only in getChat. :type invite_link: :obj:`str` :param pinned_message: Optional. The most recent pinned message (by sending date). Returned only in getChat. :type pinned_message: :class:`telebot.types.Message` - :param permissions: Optional. Default chat member permissions, for groups and supergroups. Returned only in - getChat. + :param permissions: Optional. Default chat member permissions, for groups and supergroups. Returned only in getChat. :type permissions: :class:`telebot.types.ChatPermissions` - :param slow_mode_delay: Optional. For supergroups, the minimum allowed delay between consecutive messages sent - by each unpriviledged user; in seconds. Returned only in getChat. + :param slow_mode_delay: Optional. For supergroups, the minimum allowed delay between consecutive messages sent by each unpriviledged user; in seconds. Returned only in getChat. :type slow_mode_delay: :obj:`int` - :param unrestrict_boost_count: Optional. For supergroups, the minimum number of boosts that a non-administrator - user needs to add in order to ignore slow mode and chat permissions. Returned only in getChat. + :param unrestrict_boost_count: Optional. For supergroups, the minimum number of boosts that a non-administrator user needs to add in order to ignore slow mode and chat permissions. Returned only in getChat. :type unrestrict_boost_count: :obj:`int` - :param message_auto_delete_time: Optional. The time after which all messages sent to the chat will be - automatically deleted; in seconds. Returned only in getChat. + :param message_auto_delete_time: Optional. The time after which all messages sent to the chat will be 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. + :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. + :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. + :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` - :param has_visible_history: Optional. True, if new chat members will have access to old messages; - available only to chat administrators. Returned only in getChat. + :param has_visible_history: Optional. True, if new chat members will have access to old messages; available only to chat administrators. Returned only in getChat. :type has_visible_history: :obj:`bool` :param sticker_set_name: Optional. For supergroups, name of group sticker set. Returned only in getChat. :type sticker_set_name: :obj:`str` - :param can_set_sticker_set: Optional. :obj:`bool`, if the bot can change the group sticker set. Returned only in - getChat. + :param can_set_sticker_set: Optional. :obj:`bool`, if the bot can change the group sticker set. Returned only in getChat. :type can_set_sticker_set: :obj:`bool` :param custom_emoji_sticker_set_name: Optional. For supergroups, the name of the group's custom emoji sticker set. @@ -722,12 +704,11 @@ class Chat(JsonDeserializable): signed 64 bit integer or double-precision float type are safe for storing this identifier. Returned only in getChat. :type linked_chat_id: :obj:`int` - :param location: Optional. For supergroups, the location to which the supergroup is connected. Returned only in - getChat. + :param location: Optional. For supergroups, the location to which the supergroup is connected. Returned only in getChat. :type location: :class:`telebot.types.ChatLocation` :return: Instance of the class - :rtype: :class:`telebot.types.Chat` + :rtype: :class:`telebot.types.ChatFullInfo` """ @classmethod def de_json(cls, json_string): @@ -762,7 +743,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, message_auto_delete_time=None, has_protected_content=None, sticker_set_name=None, can_set_sticker_set=None, linked_chat_id=None, location=None, join_to_send_messages=None, join_by_request=None, has_restricted_voice_and_video_messages=None, - is_forum=None, active_usernames=None, emoji_status_custom_emoji_id=None, + is_forum=None, max_reaction_count=None, active_usernames=None, emoji_status_custom_emoji_id=None, has_hidden_members=None, has_aggressive_anti_spam_enabled=None, emoji_status_expiration_date=None, available_reactions=None, accent_color_id=None, background_custom_emoji_id=None, profile_accent_color_id=None, profile_background_custom_emoji_id=None, has_visible_history=None, @@ -775,6 +756,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.first_name: str = first_name self.last_name: str = last_name self.is_forum: bool = is_forum + self.max_reaction_count: int = max_reaction_count self.photo: ChatPhoto = photo self.bio: str = bio self.join_to_send_messages: bool = join_to_send_messages @@ -812,6 +794,17 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.birthdate: Birthdate = birthdate +class Chat(ChatFullInfo): + """ + In BotAPI 7.3 Chat was reduced and full info moved to ChatFullInfo: + "Split out the class ChatFullInfo from the class Chat and changed the return type of the method getChat to ChatFullInfo." + + https://core.telegram.org/bots/api#chatfullinfo + + Currently Chat is left as full copy of ChatFullInfo for compatibility. + """ + pass + class MessageID(JsonDeserializable): """