From d6501ddc0e90c32b5f1ec89725399ea98cd85056 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 18 Aug 2021 18:47:38 +0300 Subject: [PATCH] Check and update for full compatibility to Bot API up to 5.0 --- README.md | 10 +- telebot/__init__.py | 52 ++-- telebot/apihelper.py | 4 +- telebot/types.py | 567 ++++++++++++++++++++----------------------- 4 files changed, 305 insertions(+), 328 deletions(-) diff --git a/README.md b/README.md index dbd5178..76fe327 100644 --- a/README.md +++ b/README.md @@ -591,7 +591,7 @@ You can use proxy for request. `apihelper.proxy` object will use by call `reques ```python from telebot import apihelper -apihelper.proxy = {'http':'http://10.10.1.10:3128'} +apihelper.proxy = {'http':'http://127.0.0.1:3128'} ``` If you want to use socket5 proxy you need install dependency `pip install requests[socks]` and make sure, that you have the latest version of `gunicorn`, `PySocks`, `pyTelegramBotAPI`, `requests` and `urllib3`. @@ -605,8 +605,14 @@ apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'} _Checking is in progress..._ -✅ [Bot API 4.5](https://core.telegram.org/bots/api-changelog#december-31-2019) _- To be checked..._ +✅ [Bot API 5.1](https://core.telegram.org/bots/api#march-9-2021) _- To be checked..._ +* ✔ [Bot API 5.0](https://core.telegram.org/bots/api-changelog#november-4-2020) +* ✔ [Bot API 4.9](https://core.telegram.org/bots/api-changelog#june-4-2020) +* ✔ [Bot API 4.8](https://core.telegram.org/bots/api-changelog#april-24-2020) +* ✔ [Bot API 4.7](https://core.telegram.org/bots/api-changelog#march-30-2020) +* ✔ [Bot API 4.6](https://core.telegram.org/bots/api-changelog#january-23-2020) +* ➕ [Bot API 4.5](https://core.telegram.org/bots/api-changelog#december-31-2019) - No nested MessageEntities and Markdown2 support. * ✔ [Bot API 4.4](https://core.telegram.org/bots/api-changelog#july-29-2019) * ✔ [Bot API 4.3](https://core.telegram.org/bots/api-changelog#may-31-2019) * ✔ [Bot API 4.2](https://core.telegram.org/bots/api-changelog#april-14-2019) diff --git a/telebot/__init__.py b/telebot/__init__.py index 9fd86a2..f50b932 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -145,7 +145,7 @@ class TeleBot: def __init__( self, token, parse_mode=None, threaded=True, skip_pending=False, num_threads=2, next_step_backend=None, reply_backend=None, exception_handler=None, last_update_id=0, - suppress_middleware_excepions=False # <- Typo in exceptions + suppress_middleware_excepions=False ): """ :param token: bot API token @@ -590,8 +590,9 @@ class TeleBot: if logger_level and logger_level >= logging.INFO: logger.error("Break infinity polling") - def polling(self, none_stop: bool=False, interval: int=0, timeout: int=20, - long_polling_timeout: int=20, allowed_updates: Optional[List[str]]=None): + def polling(self, non_stop: bool=False, interval: int=0, timeout: int=20, + long_polling_timeout: int=20, allowed_updates: Optional[List[str]]=None, + none_stop: Optional[bool]=None): """ This function creates a new Thread that calls an internal __retrieve_updates function. This allows the bot to retrieve Updates automagically and notify listeners and message handlers accordingly. @@ -600,7 +601,7 @@ class TeleBot: Always get updates. :param interval: Delay between two update retrivals - :param none_stop: Do not stop polling when an ApiException occurs. + :param non_stop: Do not stop polling when an ApiException occurs. :param timeout: Request connection timeout :param long_polling_timeout: Timeout in seconds for long polling (see API docs) :param allowed_updates: A list of the update types you want your bot to receive. @@ -611,12 +612,16 @@ class TeleBot: Please note that this parameter doesn't affect updates created before the call to the get_updates, so unwanted updates may be received for a short period of time. + :param none_stop: Deprecated, use non_stop. Old typo f***up compatibility :return: """ + if none_stop is not None: + non_stop = none_stop + if self.threaded: - self.__threaded_polling(none_stop, interval, timeout, long_polling_timeout, allowed_updates) + self.__threaded_polling(non_stop, interval, timeout, long_polling_timeout, allowed_updates) else: - self.__non_threaded_polling(none_stop, interval, timeout, long_polling_timeout, allowed_updates) + self.__non_threaded_polling(non_stop, interval, timeout, long_polling_timeout, allowed_updates) def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_polling_timeout = None, allowed_updates=None): logger.info('Started polling.') @@ -1118,30 +1123,34 @@ class TeleBot: thumb: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, - visible_file_name: Optional[str]=None) -> types.Message: + visible_file_name: Optional[str]=None, + disable_content_type_detection: Optional[bool]=None) -> types.Message: """ Use this method to send general files. - :param chat_id: - :param data: - :param reply_to_message_id: - :param caption: + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :param data: (document) File to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data + :param reply_to_message_id: If the message is a reply, ID of the original message + :param caption: Document caption (may also be used when resending documents by file_id), 0-1024 characters after entities parsing :param reply_markup: - :param parse_mode: - :param disable_notification: + :param parse_mode: Mode for parsing entities in the document caption + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :param timeout: - :param thumb: InputFile or String : Thumbnail of the file sent + :param thumb: InputFile or String : Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://” if the thumbnail was uploaded using multipart/form-data under :param caption_entities: :param allow_sending_without_reply: :param visible_file_name: allows to define file name that will be visible in the Telegram instead of original file name + :param disable_content_type_detection: Disables automatic server-side content type detection for files uploaded using multipart/form-data :return: API reply. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode return types.Message.de_json( apihelper.send_data( - self.token, chat_id, data, 'document', reply_to_message_id, reply_markup, - parse_mode, disable_notification, timeout, caption, thumb, caption_entities, - allow_sending_without_reply, visible_file_name)) + self.token, chat_id, data, 'document', + reply_to_message_id = reply_to_message_id, reply_markup = reply_markup, parse_mode = parse_mode, + disable_notification = disable_notification, timeout = timeout, caption = caption, thumb = thumb, + caption_entities = caption_entities, allow_sending_without_reply = allow_sending_without_reply, + disable_content_type_detection = disable_content_type_detection, visible_file_name = visible_file_name)) def send_sticker( self, chat_id: Union[int, str], data: Union[Any, str], @@ -1163,7 +1172,7 @@ class TeleBot: """ return types.Message.de_json( apihelper.send_data( - self.token, chat_id=chat_id, data=data, data_type='sticker', + self.token, chat_id, data, 'sticker', reply_to_message_id=reply_to_message_id, reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, allow_sending_without_reply=allow_sending_without_reply)) @@ -1831,7 +1840,8 @@ class TeleBot: message_id: Optional[int]=None, inline_message_id: Optional[str]=None, parse_mode: Optional[str]=None, - disable_web_page_preview: Optional[bool]=None, + entities: Optional[List[types.MessageEntity]]=None, + disable_web_page_preview: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: """ Use this method to edit text and game messages. @@ -1840,6 +1850,7 @@ class TeleBot: :param message_id: :param inline_message_id: :param parse_mode: + :param entities: :param disable_web_page_preview: :param reply_markup: :return: @@ -1847,7 +1858,7 @@ class TeleBot: parse_mode = self.parse_mode if (parse_mode is None) else parse_mode result = apihelper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, - disable_web_page_preview, reply_markup) + entities, disable_web_page_preview, reply_markup) if type(result) == bool: # if edit inline message return is bool not Message. return result return types.Message.de_json(result) @@ -2126,6 +2137,7 @@ class TeleBot: :param message_id: :param inline_message_id: :param parse_mode: + :param caption_entities: :param reply_markup: :return: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 7647e12..a3ef5c2 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1104,7 +1104,7 @@ def unpin_all_chat_messages(token, chat_id): # Updating messages def edit_message_text(token, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, - disable_web_page_preview=None, reply_markup=None): + entities = None, disable_web_page_preview=None, reply_markup=None): method_url = r'editMessageText' payload = {'text': text} if chat_id: @@ -1115,6 +1115,8 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message payload['inline_message_id'] = inline_message_id if parse_mode: payload['parse_mode'] = parse_mode + if entities: + payload['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities)) if disable_web_page_preview is not None: payload['disable_web_page_preview'] = disable_web_page_preview if reply_markup: diff --git a/telebot/types.py b/telebot/types.py index 6d17c9f..04b0d79 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -74,7 +74,7 @@ class JsonDeserializable(object): :return: Dictionary parsed from json or original dict """ if util.is_dict(json_type): - return json_type.copy() + return json_type.copy() if dict_copy else json_type elif util.is_string(json_type): return json.loads(json_type) else: @@ -302,6 +302,8 @@ class Message(JsonDeserializable): chat = Chat.de_json(obj['chat']) content_type = None opts = {} + if 'sender_chat' in obj: + opts['sender_chat'] = Chat.de_json(obj['sender_chat']) if 'forward_from' in obj: opts['forward_from'] = User.de_json(obj['forward_from']) if 'forward_from_chat' in obj: @@ -478,6 +480,7 @@ class Message(JsonDeserializable): self.from_user: User = from_user self.date: int = date self.chat: Chat = chat + self.sender_chat: Optional[Chat] = None self.forward_from: Optional[User] = None self.forward_from_chat: Optional[Chat] = None self.forward_from_message_id: Optional[int] = None @@ -969,9 +972,17 @@ class ReplyKeyboardMarkup(JsonSerializable): return json.dumps(json_dict) +class KeyboardButtonPollType(Dictionaryable): + def __init__(self, type=''): + self.type: str = type + + def to_dict(self): + return {'type': self.type} + + class KeyboardButton(Dictionaryable, JsonSerializable): def __init__(self, text: str, request_contact: Optional[bool]=None, - request_location: Optional[bool]=None, request_poll: Optional[bool]=None): + request_location: Optional[bool]=None, request_poll: Optional[KeyboardButtonPollType]=None): self.text: str = text self.request_contact: bool = request_contact self.request_location: bool = request_location @@ -991,14 +1002,6 @@ class KeyboardButton(Dictionaryable, JsonSerializable): return json_dict -class KeyboardButtonPollType(Dictionaryable): - def __init__(self, type=''): - self.type: str = type - - def to_dict(self): - return {'type': self.type} - - class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable): max_row_keys = 8 @@ -1007,7 +1010,7 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable) if json_string is None: return None obj = cls.check_json(json_string, dict_copy=False) keyboard = [[InlineKeyboardButton.de_json(button) for button in row] for row in obj['inline_keyboard']] - return cls(keyboard) + return cls(keyboard = keyboard) def __init__(self, keyboard=None, row_width=3): """ @@ -1598,9 +1601,45 @@ class ChosenInlineResult(JsonDeserializable): self.query: str = query -class InlineQueryResultArticle(JsonSerializable): - def __init__(self, id, title, input_message_content, reply_markup=None, url=None, - hide_url=None, description=None, thumb_url=None, thumb_width=None, thumb_height=None): +class InlineQueryResultBase(ABC, Dictionaryable, JsonSerializable): + # noinspection PyShadowingBuiltins + def __init__(self, type, id, title = None, caption = None, input_message_content = None, + reply_markup = None, caption_entities = None, parse_mode = None): + self.type = type + self.id = id + self.title = title + self.caption = caption + self.input_message_content = input_message_content + self.reply_markup = reply_markup + self.caption_entities = caption_entities + self.parse_mode = parse_mode + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = { + 'type': self.type, + 'id': self.id + } + if self.title: + json_dict['title'] = self.title + if self.caption: + json_dict['caption'] = self.caption + if self.input_message_content: + json_dict['input_message_content'] = self.input_message_content.to_dict() + if self.reply_markup: + json_dict['reply_markup'] = self.reply_markup.to_dict() + if self.caption_entities: + json_dict['caption_entities'] = MessageEntity.to_list_of_dicts(self.caption_entities) + if self.parse_mode: + json_dict['parse_mode'] = self.parse_mode + return json_dict + + +class InlineQueryResultArticle(InlineQueryResultBase): + def __init__(self, id, title, input_message_content, reply_markup=None, + url=None, hide_url=None, description=None, thumb_url=None, thumb_width=None, thumb_height=None): """ Represents a link to an article or web page. :param id: Unique identifier for this result, 1-64 Bytes. @@ -1615,11 +1654,7 @@ class InlineQueryResultArticle(JsonSerializable): :param thumb_height: Thumbnail height :return: """ - self.type = 'article' - self.id = id - self.title = title - self.input_message_content = input_message_content - self.reply_markup = reply_markup + super().__init__('article', id, title = title, input_message_content = input_message_content, reply_markup = reply_markup) self.url = url self.hide_url = hide_url self.description = description @@ -1627,14 +1662,8 @@ class InlineQueryResultArticle(JsonSerializable): self.thumb_width = thumb_width self.thumb_height = thumb_height - def to_json(self): - json_dict = { - 'type': self.type, - 'id': self.id, - 'title': self.title, - 'input_message_content': self.input_message_content.to_dict()} - if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dict() + def to_dict(self): + json_dict = super().to_dict() if self.url: json_dict['url'] = self.url if self.hide_url: @@ -1647,12 +1676,12 @@ class InlineQueryResultArticle(JsonSerializable): json_dict['thumb_width'] = self.thumb_width if self.thumb_height: json_dict['thumb_height'] = self.thumb_height - return json.dumps(json_dict) + return json_dict -class InlineQueryResultPhoto(JsonSerializable): +class InlineQueryResultPhoto(InlineQueryResultBase): def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None, title=None, - description=None, caption=None, parse_mode=None, reply_markup=None, input_message_content=None): + description=None, caption=None, caption_entities=None, parse_mode=None, reply_markup=None, input_message_content=None): """ Represents a link to a photo. :param id: Unique identifier for this result, 1-64 bytes @@ -1669,43 +1698,32 @@ class InlineQueryResultPhoto(JsonSerializable): :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo :return: """ - self.type = 'photo' - self.id = id + super().__init__('photo', id, title = title, caption = caption, + input_message_content = input_message_content, reply_markup = reply_markup, + parse_mode = parse_mode, caption_entities = caption_entities) self.photo_url = photo_url + self.thumb_url = thumb_url self.photo_width = photo_width self.photo_height = photo_height - self.thumb_url = thumb_url - self.title = title self.description = description - self.caption = caption - self.parse_mode = parse_mode - self.reply_markup = reply_markup - self.input_message_content = input_message_content - def to_json(self): - json_dict = {'type': self.type, 'id': self.id, 'photo_url': self.photo_url, 'thumb_url': self.thumb_url} + def to_dict(self): + json_dict = super().to_dict() + json_dict['photo_url'] = self.photo_url + json_dict['thumb_url'] = self.thumb_url if self.photo_width: json_dict['photo_width'] = self.photo_width if self.photo_height: json_dict['photo_height'] = self.photo_height - if self.title: - json_dict['title'] = self.title if self.description: json_dict['description'] = self.description - if self.caption: - json_dict['caption'] = self.caption - if self.parse_mode: - json_dict['parse_mode'] = self.parse_mode - if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dict() - if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dict() - return json.dumps(json_dict) + return json_dict -class InlineQueryResultGif(JsonSerializable): - def __init__(self, id, gif_url, thumb_url, gif_width=None, gif_height=None, title=None, caption=None, - reply_markup=None, input_message_content=None, gif_duration=None): +class InlineQueryResultGif(InlineQueryResultBase): + def __init__(self, id, gif_url, thumb_url, gif_width=None, gif_height=None, + title=None, caption=None, caption_entities=None, + reply_markup=None, input_message_content=None, gif_duration=None, parse_mode=None): """ Represents a link to an animated GIF file. :param id: Unique identifier for this result, 1-64 bytes. @@ -1719,39 +1737,31 @@ class InlineQueryResultGif(JsonSerializable): :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo :return: """ - self.type = 'gif' - self.id = id + super().__init__('gif', id, title = title, caption = caption, + input_message_content = input_message_content, reply_markup = reply_markup, + parse_mode = parse_mode, caption_entities = caption_entities) self.gif_url = gif_url self.gif_width = gif_width self.gif_height = gif_height self.thumb_url = thumb_url - self.title = title - self.caption = caption - self.reply_markup = reply_markup - self.input_message_content = input_message_content self.gif_duration = gif_duration - def to_json(self): - json_dict = {'type': self.type, 'id': self.id, 'gif_url': self.gif_url, 'thumb_url': self.thumb_url} - if self.gif_height: - json_dict['gif_height'] = self.gif_height + def to_dict(self): + json_dict = super().to_dict() + json_dict['gif_url'] = self.gif_url if self.gif_width: json_dict['gif_width'] = self.gif_width - if self.title: - json_dict['title'] = self.title - if self.caption: - json_dict['caption'] = self.caption - if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dict() - if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dict() + if self.gif_height: + json_dict['gif_height'] = self.gif_height + json_dict['thumb_url'] = self.thumb_url if self.gif_duration: json_dict['gif_duration'] = self.gif_duration - return json.dumps(json_dict) + return json_dict -class InlineQueryResultMpeg4Gif(JsonSerializable): - def __init__(self, id, mpeg4_url, thumb_url, mpeg4_width=None, mpeg4_height=None, title=None, caption=None, +class InlineQueryResultMpeg4Gif(InlineQueryResultBase): + def __init__(self, id, mpeg4_url, thumb_url, mpeg4_width=None, mpeg4_height=None, + title=None, caption=None, caption_entities=None, parse_mode=None, reply_markup=None, input_message_content=None, mpeg4_duration=None): """ Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). @@ -1768,43 +1778,32 @@ class InlineQueryResultMpeg4Gif(JsonSerializable): :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo :return: """ - self.type = 'mpeg4_gif' - self.id = id + super().__init__('mpeg4_gif', id, title = title, caption = caption, + input_message_content = input_message_content, reply_markup = reply_markup, + parse_mode = parse_mode, caption_entities = caption_entities) self.mpeg4_url = mpeg4_url self.mpeg4_width = mpeg4_width self.mpeg4_height = mpeg4_height self.thumb_url = thumb_url - self.title = title - self.caption = caption - self.parse_mode = parse_mode - self.reply_markup = reply_markup - self.input_message_content = input_message_content self.mpeg4_duration = mpeg4_duration - def to_json(self): - json_dict = {'type': self.type, 'id': self.id, 'mpeg4_url': self.mpeg4_url, 'thumb_url': self.thumb_url} + def to_dict(self): + json_dict = super().to_dict() + json_dict['mpeg4_url'] = self.mpeg4_url if self.mpeg4_width: json_dict['mpeg4_width'] = self.mpeg4_width if self.mpeg4_height: json_dict['mpeg4_height'] = self.mpeg4_height - if self.title: - json_dict['title'] = self.title - if self.caption: - json_dict['caption'] = self.caption - if self.parse_mode: - json_dict['parse_mode'] = self.parse_mode - if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dict() - if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dict() + json_dict['thumb_url'] = self.thumb_url if self.mpeg4_duration: json_dict['mpeg4_duration '] = self.mpeg4_duration - return json.dumps(json_dict) + return json_dict -class InlineQueryResultVideo(JsonSerializable): - def __init__(self, id, video_url, mime_type, thumb_url, title, - caption=None, parse_mode=None, video_width=None, video_height=None, video_duration=None, +class InlineQueryResultVideo(InlineQueryResultBase): + def __init__(self, id, video_url, mime_type, thumb_url, + title, caption=None, caption_entities=None, parse_mode=None, + video_width=None, video_height=None, video_duration=None, description=None, reply_markup=None, input_message_content=None): """ Represents link to a page containing an embedded video player or a video file. @@ -1821,129 +1820,87 @@ class InlineQueryResultVideo(JsonSerializable): :param description: Short description of the result :return: """ - self.type = 'video' - self.id = id + super().__init__('video', id, title = title, caption = caption, + input_message_content = input_message_content, reply_markup = reply_markup, + parse_mode = parse_mode, caption_entities = caption_entities) self.video_url = video_url self.mime_type = mime_type + self.thumb_url = thumb_url self.video_width = video_width self.video_height = video_height self.video_duration = video_duration - self.thumb_url = thumb_url - self.title = title - self.caption = caption - self.parse_mode = parse_mode self.description = description - self.input_message_content = input_message_content - self.reply_markup = reply_markup - def to_json(self): - json_dict = {'type': self.type, 'id': self.id, 'video_url': self.video_url, 'mime_type': self.mime_type, - 'thumb_url': self.thumb_url, 'title': self.title} - if self.video_width: - json_dict['video_width'] = self.video_width + def to_dict(self): + json_dict = super().to_dict() + json_dict['video_url'] = self.video_url + json_dict['mime_type'] = self.mime_type + json_dict['thumb_url'] = self.thumb_url if self.video_height: json_dict['video_height'] = self.video_height if self.video_duration: json_dict['video_duration'] = self.video_duration if self.description: json_dict['description'] = self.description - if self.caption: - json_dict['caption'] = self.caption - if self.parse_mode: - json_dict['parse_mode'] = self.parse_mode - if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dict() - if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dict() - return json.dumps(json_dict) + return json_dict -class InlineQueryResultAudio(JsonSerializable): - def __init__(self, id, audio_url, title, caption=None, parse_mode=None, performer=None, audio_duration=None, - reply_markup=None, input_message_content=None): - self.type = 'audio' - self.id = id +class InlineQueryResultAudio(InlineQueryResultBase): + def __init__(self, id, audio_url, title, + caption=None, caption_entities=None, parse_mode=None, performer=None, + audio_duration=None, reply_markup=None, input_message_content=None): + super().__init__('audio', id, title = title, caption = caption, + input_message_content = input_message_content, reply_markup = reply_markup, + parse_mode = parse_mode, caption_entities = caption_entities) self.audio_url = audio_url - self.title = title - self.caption = caption - self.parse_mode = parse_mode self.performer = performer self.audio_duration = audio_duration - self.reply_markup = reply_markup - self.input_message_content = input_message_content - def to_json(self): - json_dict = {'type': self.type, 'id': self.id, 'audio_url': self.audio_url, 'title': self.title} - if self.caption: - json_dict['caption'] = self.caption - if self.parse_mode: - json_dict['parse_mode'] = self.parse_mode + def to_dict(self): + json_dict = super().to_dict() + json_dict['audio_url'] = self.audio_url if self.performer: json_dict['performer'] = self.performer if self.audio_duration: json_dict['audio_duration'] = self.audio_duration - if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dict() - if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dict() - return json.dumps(json_dict) + return json_dict -class InlineQueryResultVoice(JsonSerializable): - def __init__(self, id, voice_url, title, caption=None, parse_mode=None, performer=None, voice_duration=None, - reply_markup=None, input_message_content=None): - self.type = 'voice' - self.id = id +class InlineQueryResultVoice(InlineQueryResultBase): + def __init__(self, id, voice_url, title, caption=None, caption_entities=None, + parse_mode=None, voice_duration=None, reply_markup=None, input_message_content=None): + super().__init__('voice', id, title = title, caption = caption, + input_message_content = input_message_content, reply_markup = reply_markup, + parse_mode = parse_mode, caption_entities = caption_entities) self.voice_url = voice_url - self.title = title - self.caption = caption - self.parse_mode = parse_mode - self.performer = performer self.voice_duration = voice_duration - self.reply_markup = reply_markup - self.input_message_content = input_message_content - def to_json(self): - json_dict = {'type': self.type, 'id': self.id, 'voice_url': self.voice_url, 'title': self.title} - if self.caption: - json_dict['caption'] = self.caption - if self.parse_mode: - json_dict['parse_mode'] = self.parse_mode - if self.performer: - json_dict['performer'] = self.performer + def to_dict(self): + json_dict = super().to_dict() + json_dict['voice_url'] = self.voice_url if self.voice_duration: json_dict['voice_duration'] = self.voice_duration - if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dict() - if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dict() - return json.dumps(json_dict) + return json_dict -class InlineQueryResultDocument(JsonSerializable): - def __init__(self, id, title, document_url, mime_type, caption=None, parse_mode=None, description=None, - reply_markup=None, input_message_content=None, thumb_url=None, thumb_width=None, thumb_height=None): - self.type = 'document' - self.id = id - self.title = title +class InlineQueryResultDocument(InlineQueryResultBase): + def __init__(self, id, title, document_url, mime_type, caption=None, caption_entities=None, + parse_mode=None, description=None, reply_markup=None, input_message_content=None, + thumb_url=None, thumb_width=None, thumb_height=None): + super().__init__('document', id, title = title, caption = caption, + input_message_content = input_message_content, reply_markup = reply_markup, + parse_mode = parse_mode, caption_entities = caption_entities) self.document_url = document_url self.mime_type = mime_type - self.caption = caption - self.parse_mode = parse_mode self.description = description - self.reply_markup = reply_markup - self.input_message_content = input_message_content self.thumb_url = thumb_url self.thumb_width = thumb_width self.thumb_height = thumb_height - def to_json(self): - json_dict = {'type': self.type, 'id': self.id, 'title': self.title, 'document_url': self.document_url, - 'mime_type': self.mime_type} - if self.caption: - json_dict['caption'] = self.caption - if self.parse_mode: - json_dict['parse_mode'] = self.parse_mode + def to_dict(self): + json_dict = super().to_dict() + json_dict['document_url'] = self.document_url + json_dict['mime_type'] = self.mime_type if self.description: json_dict['description'] = self.description if self.thumb_url: @@ -1952,129 +1909,127 @@ class InlineQueryResultDocument(JsonSerializable): json_dict['thumb_width'] = self.thumb_width if self.thumb_height: json_dict['thumb_height'] = self.thumb_height - if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dict() - if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dict() - return json.dumps(json_dict) + return json_dict -class InlineQueryResultLocation(JsonSerializable): +class InlineQueryResultLocation(InlineQueryResultBase): def __init__(self, id, title, latitude, longitude, horizontal_accuracy, live_period=None, reply_markup=None, - input_message_content=None, thumb_url=None, thumb_width=None, thumb_height=None): - self.type = 'location' - self.id = id - self.title = title + input_message_content=None, thumb_url=None, thumb_width=None, thumb_height=None, heading=None, proximity_alert_radius = None): + super().__init__('location', id, title = title, + input_message_content = input_message_content, reply_markup = reply_markup) self.latitude = latitude self.longitude = longitude self.horizontal_accuracy = horizontal_accuracy self.live_period = live_period - self.reply_markup = reply_markup - self.input_message_content = input_message_content + self.heading: int = heading + self.proximity_alert_radius: int = proximity_alert_radius self.thumb_url = thumb_url self.thumb_width = thumb_width self.thumb_height = thumb_height - def to_json(self): - json_dict = {'type': self.type, 'id': self.id, 'latitude': self.latitude, 'longitude': self.longitude, - 'title': self.title} + def to_dict(self): + json_dict = super().to_dict() + json_dict['latitude'] = self.latitude + json_dict['longitude'] = self.longitude if self.horizontal_accuracy: json_dict['horizontal_accuracy'] = self.horizontal_accuracy if self.live_period: json_dict['live_period'] = self.live_period + if self.heading: + json_dict['heading'] = self.heading + if self.proximity_alert_radius: + json_dict['proximity_alert_radius'] = self.proximity_alert_radius if self.thumb_url: json_dict['thumb_url'] = self.thumb_url if self.thumb_width: json_dict['thumb_width'] = self.thumb_width if self.thumb_height: json_dict['thumb_height'] = self.thumb_height - if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dict() - if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dict() - return json.dumps(json_dict) + return json_dict -class InlineQueryResultVenue(JsonSerializable): +class InlineQueryResultVenue(InlineQueryResultBase): def __init__(self, id, title, latitude, longitude, address, foursquare_id=None, foursquare_type=None, reply_markup=None, input_message_content=None, thumb_url=None, thumb_width=None, thumb_height=None, google_place_id=None, google_place_type=None): - self.type = 'venue' - self.id = id - self.title = title + super().__init__('venue', id, title = title, + input_message_content = input_message_content, reply_markup = reply_markup) self.latitude = latitude self.longitude = longitude self.address = address self.foursquare_id = foursquare_id self.foursquare_type = foursquare_type - self.reply_markup = reply_markup - self.input_message_content = input_message_content + self.google_place_id = google_place_id + self.google_place_type = google_place_type self.thumb_url = thumb_url self.thumb_width = thumb_width self.thumb_height = thumb_height - self.google_place_id = google_place_id - self.google_place_type = google_place_type - def to_json(self): - json_dict = {'type': self.type, 'id': self.id, 'title': self.title, 'latitude': self.latitude, - 'longitude': self.longitude, 'address': self.address} + def to_dict(self): + json_dict = super().to_dict() + json_dict['latitude'] = self.latitude + json_dict['longitude'] = self.longitude + json_dict['address'] = self.address if self.foursquare_id: json_dict['foursquare_id'] = self.foursquare_id if self.foursquare_type: json_dict['foursquare_type'] = self.foursquare_type - if self.thumb_url: - json_dict['thumb_url'] = self.thumb_url - if self.thumb_width: - json_dict['thumb_width'] = self.thumb_width - if self.thumb_height: - json_dict['thumb_height'] = self.thumb_height - if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dict() - if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dict() if self.google_place_id: json_dict['google_place_id'] = self.google_place_id if self.google_place_type: json_dict['google_place_type'] = self.google_place_type - return json.dumps(json_dict) - - -class InlineQueryResultContact(JsonSerializable): - def __init__(self, id, phone_number, first_name, last_name=None, vcard=None, - reply_markup=None, input_message_content=None, - thumb_url=None, thumb_width=None, thumb_height=None): - self.type = 'contact' - self.id = id - self.phone_number = phone_number - self.first_name = first_name - self.last_name = last_name - self.vcard = vcard - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height - - def to_json(self): - json_dict = {'type': self.type, 'id': self.id, 'phone_number': self.phone_number, 'first_name': self.first_name} - if self.last_name: - json_dict['last_name'] = self.last_name - if self.vcard: - json_dict['vcard'] = self.vcard - if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dict() - if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dict() if self.thumb_url: json_dict['thumb_url'] = self.thumb_url if self.thumb_width: json_dict['thumb_width'] = self.thumb_width if self.thumb_height: json_dict['thumb_height'] = self.thumb_height - return json.dumps(json_dict) + return json_dict -class BaseInlineQueryResultCached(JsonSerializable): +class InlineQueryResultContact(InlineQueryResultBase): + def __init__(self, id, phone_number, first_name, last_name=None, vcard=None, + reply_markup=None, input_message_content=None, + thumb_url=None, thumb_width=None, thumb_height=None): + super().__init__('contact', id, + input_message_content = input_message_content, reply_markup = reply_markup) + self.phone_number = phone_number + self.first_name = first_name + self.last_name = last_name + self.vcard = vcard + self.thumb_url = thumb_url + self.thumb_width = thumb_width + self.thumb_height = thumb_height + + def to_dict(self): + json_dict = super().to_dict() + json_dict['phone_number'] = self.phone_number + json_dict['first_name'] = self.first_name + if self.last_name: + json_dict['last_name'] = self.last_name + if self.vcard: + json_dict['vcard'] = self.vcard + if self.thumb_url: + json_dict['thumb_url'] = self.thumb_url + if self.thumb_width: + json_dict['thumb_width'] = self.thumb_width + if self.thumb_height: + json_dict['thumb_height'] = self.thumb_height + return json_dict + + +class InlineQueryResultGame(InlineQueryResultBase): + def __init__(self, id, game_short_name, reply_markup=None): + super().__init__('game', id, reply_markup = reply_markup) + self.game_short_name = game_short_name + + def to_dict(self): + json_dict = super().to_dict() + json_dict['game_short_name'] = self.game_short_name + return json_dict + + +class InlineQueryResultCachedBase(ABC, JsonSerializable): def __init__(self): self.type = None self.id = None @@ -2084,6 +2039,7 @@ class BaseInlineQueryResultCached(JsonSerializable): self.reply_markup = None self.input_message_content = None self.parse_mode = None + self.caption_entities = None self.payload_dic = {} def to_json(self): @@ -2102,60 +2058,68 @@ class BaseInlineQueryResultCached(JsonSerializable): json_dict['input_message_content'] = self.input_message_content.to_dict() if self.parse_mode: json_dict['parse_mode'] = self.parse_mode + if self.caption_entities: + json_dict['caption_entities'] = MessageEntity.to_list_of_dicts(self.caption_entities) return json.dumps(json_dict) -class InlineQueryResultCachedPhoto(BaseInlineQueryResultCached): - def __init__(self, id, photo_file_id, title=None, description=None, caption=None, parse_mode=None, +class InlineQueryResultCachedPhoto(InlineQueryResultCachedBase): + def __init__(self, id, photo_file_id, title=None, description=None, + caption=None, caption_entities = None, parse_mode=None, reply_markup=None, input_message_content=None): - BaseInlineQueryResultCached.__init__(self) + InlineQueryResultCachedBase.__init__(self) self.type = 'photo' self.id = id self.photo_file_id = photo_file_id self.title = title self.description = description self.caption = caption + self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content self.parse_mode = parse_mode self.payload_dic['photo_file_id'] = photo_file_id -class InlineQueryResultCachedGif(BaseInlineQueryResultCached): - def __init__(self, id, gif_file_id, title=None, description=None, caption=None, parse_mode=None, reply_markup=None, - input_message_content=None): - BaseInlineQueryResultCached.__init__(self) +class InlineQueryResultCachedGif(InlineQueryResultCachedBase): + def __init__(self, id, gif_file_id, title=None, description=None, + caption=None, caption_entities = None, parse_mode=None, + reply_markup=None, input_message_content=None): + InlineQueryResultCachedBase.__init__(self) self.type = 'gif' self.id = id self.gif_file_id = gif_file_id self.title = title self.description = description self.caption = caption + self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content self.parse_mode = parse_mode self.payload_dic['gif_file_id'] = gif_file_id -class InlineQueryResultCachedMpeg4Gif(BaseInlineQueryResultCached): - def __init__(self, id, mpeg4_file_id, title=None, description=None, caption=None, parse_mode=None, +class InlineQueryResultCachedMpeg4Gif(InlineQueryResultCachedBase): + def __init__(self, id, mpeg4_file_id, title=None, description=None, + caption=None, caption_entities = None, parse_mode=None, reply_markup=None, input_message_content=None): - BaseInlineQueryResultCached.__init__(self) + InlineQueryResultCachedBase.__init__(self) self.type = 'mpeg4_gif' self.id = id self.mpeg4_file_id = mpeg4_file_id self.title = title self.description = description self.caption = caption + self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content self.parse_mode = parse_mode self.payload_dic['mpeg4_file_id'] = mpeg4_file_id -class InlineQueryResultCachedSticker(BaseInlineQueryResultCached): +class InlineQueryResultCachedSticker(InlineQueryResultCachedBase): def __init__(self, id, sticker_file_id, reply_markup=None, input_message_content=None): - BaseInlineQueryResultCached.__init__(self) + InlineQueryResultCachedBase.__init__(self) self.type = 'sticker' self.id = id self.sticker_file_id = sticker_file_id @@ -2164,60 +2128,68 @@ class InlineQueryResultCachedSticker(BaseInlineQueryResultCached): self.payload_dic['sticker_file_id'] = sticker_file_id -class InlineQueryResultCachedDocument(BaseInlineQueryResultCached): - def __init__(self, id, document_file_id, title, description=None, caption=None, parse_mode=None, reply_markup=None, - input_message_content=None): - BaseInlineQueryResultCached.__init__(self) +class InlineQueryResultCachedDocument(InlineQueryResultCachedBase): + def __init__(self, id, document_file_id, title, description=None, + caption=None, caption_entities = None, parse_mode=None, + reply_markup=None, input_message_content=None): + InlineQueryResultCachedBase.__init__(self) self.type = 'document' self.id = id self.document_file_id = document_file_id self.title = title self.description = description self.caption = caption + self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content self.parse_mode = parse_mode self.payload_dic['document_file_id'] = document_file_id -class InlineQueryResultCachedVideo(BaseInlineQueryResultCached): - def __init__(self, id, video_file_id, title, description=None, caption=None, parse_mode=None, reply_markup=None, +class InlineQueryResultCachedVideo(InlineQueryResultCachedBase): + def __init__(self, id, video_file_id, title, description=None, + caption=None, caption_entities = None, parse_mode=None, + reply_markup=None, input_message_content=None): - BaseInlineQueryResultCached.__init__(self) + InlineQueryResultCachedBase.__init__(self) self.type = 'video' self.id = id self.video_file_id = video_file_id self.title = title self.description = description self.caption = caption + self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content self.parse_mode = parse_mode self.payload_dic['video_file_id'] = video_file_id -class InlineQueryResultCachedVoice(BaseInlineQueryResultCached): - def __init__(self, id, voice_file_id, title, caption=None, parse_mode=None, reply_markup=None, - input_message_content=None): - BaseInlineQueryResultCached.__init__(self) +class InlineQueryResultCachedVoice(InlineQueryResultCachedBase): + def __init__(self, id, voice_file_id, title, caption=None, caption_entities = None, + parse_mode=None, reply_markup=None, input_message_content=None): + InlineQueryResultCachedBase.__init__(self) self.type = 'voice' self.id = id self.voice_file_id = voice_file_id self.title = title self.caption = caption + self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content self.parse_mode = parse_mode self.payload_dic['voice_file_id'] = voice_file_id -class InlineQueryResultCachedAudio(BaseInlineQueryResultCached): - def __init__(self, id, audio_file_id, caption=None, parse_mode=None, reply_markup=None, input_message_content=None): - BaseInlineQueryResultCached.__init__(self) +class InlineQueryResultCachedAudio(InlineQueryResultCachedBase): + def __init__(self, id, audio_file_id, caption=None, caption_entities = None, + parse_mode=None, reply_markup=None, input_message_content=None): + InlineQueryResultCachedBase.__init__(self) self.type = 'audio' self.id = id self.audio_file_id = audio_file_id self.caption = caption + self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content self.parse_mode = parse_mode @@ -2226,20 +2198,6 @@ class InlineQueryResultCachedAudio(BaseInlineQueryResultCached): # Games -class InlineQueryResultGame(JsonSerializable): - def __init__(self, id, game_short_name, reply_markup=None): - self.type = 'game' - self.id = id - self.game_short_name = game_short_name - self.reply_markup = reply_markup - - def to_json(self): - json_dic = {'type': self.type, 'id': self.id, 'game_short_name': self.game_short_name} - if self.reply_markup: - json_dic['reply_markup'] = self.reply_markup.to_dict() - return json.dumps(json_dic) - - class Game(JsonDeserializable): @classmethod def de_json(cls, json_string): @@ -2553,7 +2511,7 @@ class InputMedia(Dictionaryable, JsonSerializable): if self.parse_mode: json_dict['parse_mode'] = self.parse_mode if self.caption_entities: - json_dict['caption_entities'] = [MessageEntity.to_dict(entity) for entity in self.caption_entities] + json_dict['caption_entities'] = MessageEntity.to_list_of_dicts(self.caption_entities) return json_dict def convert_input_media(self): @@ -2817,9 +2775,9 @@ class VoiceChatScheduled(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string, dict_copy=False) - return cls(obj['start_date']) + return cls(**obj) - def __init__(self, start_date): + def __init__(self, start_date, **kwargs): self.start_date: int = start_date @@ -2828,9 +2786,9 @@ class VoiceChatEnded(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string, dict_copy=False) - return cls(obj['duration']) + return cls(**obj) - def __init__(self, duration): + def __init__(self, duration, **kwargs): self.duration: int = duration @@ -2839,12 +2797,11 @@ class VoiceChatParticipantsInvited(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - users = None if 'users' in obj: - users = [User.de_json(u) for u in obj['users']] - return cls(users) + obj['users'] = [User.de_json(u) for u in obj['users']] + return cls(**obj) - def __init__(self, users=None): + def __init__(self, users=None, **kwargs): self.users: List[User] = users @@ -2853,7 +2810,7 @@ class MessageAutoDeleteTimerChanged(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string, dict_copy=False) - return cls(obj['message_auto_delete_time']) + return cls(**obj) - def __init__(self, message_auto_delete_time): + def __init__(self, message_auto_delete_time, **kwargs): self.message_auto_delete_time = message_auto_delete_time